.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──┘
将匹配ab
,然后它将尝试匹配左侧的*
(必要时回溯)当然,在这个简单的例子中,在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>─┘