.net 为什么这个反向引用在lookback中不起作用?

.net 为什么这个反向引用在lookback中不起作用?,.net,regex,regex-lookarounds,.net,Regex,Regex Lookarounds,使用反向引用匹配正则表达式中的重复字符很简单: (.)\1 但是,我想在这对字符之后匹配字符,所以我想我可以简单地将其放在一个lookback中: (?<=(.)\1). (?简短版本:Lookbehinds从右到左匹配。这意味着当正则表达式引擎遇到\1时,它还没有捕获到该组中的任何内容,因此正则表达式总是失败。解决方案非常简单: (?<=\1(.)). 但是,这似乎根本没有使用修饰符。事实上,如果我们将修饰符放在前面: (?<=(?i)a). (您可以在“表”选项卡

使用反向引用匹配正则表达式中的重复字符很简单:

(.)\1

但是,我想在这对字符之后匹配字符,所以我想我可以简单地将其放在一个lookback中:

(?<=(.)\1).

(?简短版本:Lookbehinds从右到左匹配。这意味着当正则表达式引擎遇到
\1
时,它还没有捕获到该组中的任何内容,因此正则表达式总是失败。解决方案非常简单:

(?<=\1(.)).
但是,这似乎根本没有使用修饰符。事实上,如果我们将修饰符放在前面:

(?<=(?i)a).
(您可以在“表”选项卡上看到捕获。)再一次,“从右到左应用lookbehinds”并不是全部内容

因此,这篇文章试图成为一篇关于.NET中正则表达式方向性的全面参考,因为我不知道有任何这样的资源。在.NET中阅读复杂正则表达式的诀窍是在三到四个过程中完成。除了最后一个过程外,所有过程都是从左到右的,不管lookbehinds或
RegexOptions.RightToLeft
。我相信是这样的,因为.NET在解析和编译正则表达式时会处理这些

第一遍:内联修改器 这基本上就是上面的示例所示。如果在正则表达式中的任何地方,您都有以下代码片段:

...a(b(?i)c)d...
无论该模式位于何处或是否使用RTL选项,
c
将不区分大小写,而
a
b
d
将不区分大小写(前提是它们不受其他前面或全局修饰符的影响)。这可能是最简单的规则

第二遍:组号[未命名组] 对于此过程,您应该完全忽略模式中的任何命名组,即
(?)形式的组

第三遍:组号[命名组] 当然,如果正则表达式中没有命名组,则可以完全跳过此过程

一个鲜为人知的特性是,命名组在.NET中也有(隐式)组号,可以在
Regex.Replace
的反向引用和替换模式中使用。一旦处理完所有未命名组,这些组将在单独的过程中获得其编号。给它们编号的规则如下:

  • 当一个名称第一次出现时,该组将获得第一个未使用的编号。同样,如果正则表达式使用显式编号,这可能是已使用编号的差距,或者可能比迄今为止最大的组编号大一个。这会将此新编号与当前名称永久关联
  • 因此,当一个名称再次出现在正则表达式中时,该组将具有上次用于该名称的相同编号
这是一个包含所有三种类型组的更完整示例,明确显示了过程2和过程3:

         (?<a>.)(.)(.)(?<b>.)(?<a>.)(?<5>.)(.)(?<c>.)
Pass 2:  │     │└1┘└2┘│     ││     │└──5──┘└3┘│     │
Pass 3:  └──4──┘      └──6──┘└──4──┘          └──7──┘
将匹配a
b
,然后它将尝试匹配左侧的
*
(必要时回溯)当然,在这个简单的例子中,在LTR和RTL模式之间的结果是相同的,但是它有助于有意识地跟踪引擎在回溯中的作用。它可以改变一些与不贪婪的修饰符一样简单的东西。考虑ReGEX < /P>
a.*?b
相反,我们正在尝试匹配
axxbxbxb
。在LTR模式下,您会得到预期的匹配
axxb
,因为不灵活的量词满足
xx
。但是,在RTL模式下,您实际上会匹配整个字符串,因为第一个
b
位于字符串末尾,但是
*?
需要匹配所有
xxbxx
a
以匹配

很明显,它也会对反向引用产生影响,正如问题和答案顶部的示例所示。在LTR模式下,我们使用
(.)\1
匹配重复字符,在RTL模式下,我们使用
\1(.)
,因为我们需要确保正则表达式引擎在尝试引用它之前遇到捕获

考虑到这一点,我们可以从新的角度看待lookarounds。当正则表达式引擎遇到lookarounds时,它将按如下方式处理它:

  • 它记住其在目标字符串中的当前位置
    x
    ,以及当前处理方向
  • 现在它强制RTL模式,不管它当前处于何种模式
  • 然后从当前位置
    x
    开始,从右到左匹配lookback的内容
  • 一旦lookback被完全处理,如果它通过,正则表达式引擎的位置将重置为位置
    x
    ,并恢复原始处理方向
而前瞻似乎更无害(因为我们几乎从未遇到过与他们相关的问题),它的行为实际上是几乎相同的,只是它强制LTR模式。当然,在大多数仅LTR的模式中,这一点从未被注意到。但是如果正则表达式本身在RTL模式中匹配,或者我们正在做一些疯狂的事情,比如将前瞻放在前瞻中,那么前瞻将改变处理方向,就像ookbehind有

那么,你应该如何真正阅读一个做了这样有趣事情的正则表达式呢?第一步是将它分成单独的组件,通常是单独的标记及其相关的量词。然后根据正则表达式是LTR还是RTL,分别从上到下或从下到上。每当你遇到环视在这个过程中,检查它的朝向,跳到正确的一端,然后从那里读取环视。完成环视后,继续周围的图案

当然还有另一个问题……当你遇到一个替代
(..|…|…)
,即使在RTL匹配过程中,也总是从左到右尝试替代方案
...a(b(?i)c)d...
(a)(?<=(b)(?=(.)).((c).(d)))(e)
└1┘    └2┘   └3┘  │└5┘ └6┘│ └7┘
                  └───4───┘
(a)(?<1>b)(?<2>c)(d)(e)(?<6>f)(g)(h)
└1┘└──1──┘└──2──┘└3┘└4┘└──6──┘└5┘└7┘
         (?<a>.)(.)(.)(?<b>.)(?<a>.)(?<5>.)(.)(?<c>.)
Pass 2:  │     │└1┘└2┘│     ││     │└──5──┘└3┘│     │
Pass 3:  └──4──┘      └──6──┘└──4──┘          └──7──┘
a.*b
a.*?b
.+(?=.(?<=a.+).).(?<=.(?<=b.|c.)..(?=d.|.+(?<=ab*?))).
LTR             RTL

 1  .+          18
    (?=
 2    .         14
      (?<=
 4      a       16
 3      .+      17
      )
 5    .         13
    )
 6  .           13
    (?<=
17    .         12
      (?<=
14      b        9
13      .        8
      |
16      c       11
15      .       10
      )
12    ..         7
      (?=
 7      d        2
 8      .        3
      |
 9      .+       4
        (?<=
11        a      6
10        b*?    5
        )
      )
    )
18  .            1
(?<a>fgh).{8}(?<=(?<b-a>.{3}).{2})
abcdefghijklmnopqrstuvwxyz
   └──<a>──┘  └──<b-a>──┘
(?<a>d.{8}).+$(?<=(?<b-a>.{11}).)
        Example:              Pushes onto <b>:    Possible regex:

abcdefghijklmnopqrstuvwxyz    ""                  (?<a>d.{8}).+$(?<=(?<b-a>.{11})...)
   └──<a>──┘└──<b-a>──┘

abcdefghijklmnopqrstuvwxyz    "jkl"               (?<a>d.{8}).+$(?<=(?<b-a>.{11}).{6})
   └──<a>┼─┘       │
         └──<b-a>──┘

abcdefghijklmnopqrstuvwxyz    "klmnopq"           (?<a>k.{8})(?<=(?<b-a>.{11})..)
      │   └──<a>┼─┘
      └──<b-a>──┘

abcdefghijklmnopqrstuvwxyz    ""                  (?<=(?<b-a>.{7})(?<a>.{4}o))
   └<b-a>┘└<a>┘

abcdefghijklmnopqrstuvwxyz    "fghijklmn"         (?<a>d.{12})(?<=(?<b-a>.{9})..)
   └─┼──<a>──┼─┘
     └─<b-a>─┘

abcdefghijklmnopqrstuvwxyz    "cdefg"             (?<a>c.{4})..(?<=(?<b-a>.{9}))
│ └<a>┘ │
└─<b-a>─┘