这是使用.NET正则表达式引擎。
我试图在lookbehind子句中使用条件。当表达式在lookbehind之外使用时,它的行为与我期望的一样 - 但是当放置在lookbehind中时,它具有不同的行为。
以下是尝试复制问题的效果的简单示例。
匹配
good morning
使用正则表达式:
(?<=(?(?=go)good|bad)\s)morning
不会产生任何匹配。
如果没有看后面的尝试:
(?(?=go)good|bad)\smorning
我得到一场比赛&#34;早上好&#34;
通过摆弄,我发现前瞻性光标位置,当它在lookbehind内时,是在&#34; good&#34;:
之后(?<=(?(?=\smor)good|bad)\s)morning
这匹配&#34;早上&#34;。
我的问题是这是预期还是某种错误?
显然这个例子不是现实世界 - 当我偶然发现这个问题时我试图解决的问题如下:表达式使用条件来确定下一个单词的长度,然后使用两组不同的规则用于匹配该单词。类似于:
(?<=\s+(?(?=[^\s]{1,2}\s)[A-Z0-9]+|(?![A-Z]+\s)[0-9-A-Z/"']+))\s+matching\s+text
匹配&#34;匹配的文字&#34;只有当一个或两个字母单词由字母和数字组成,或者一个较长的单词不仅包含字母而且可以包含数字,字母,斜杠,短划线,引号和撇号时。
以下内容应匹配&#34;匹配文字&#34;:
1 matching text
a matching text
它只匹配第一个,因为条件评估为假(它正在查看&#34;匹配&#34;而不是&#34; a&#34;)和负面展望搜索a包含所有字母的单词在&#34; a&#34;。
上失败进一步的例子:
必须匹配&#34;匹配文字&#34;:
123-1 matching text
9B matching text
15/16 matching text
"45" matching text
A matching text
AA matching text
A1 matching text
不得匹配&#34;匹配文字&#34;
and matching text
" matching text
A- matching text
答案 0 :(得分:3)
您的问题非常适合深入了解外观和前瞻。这是一个很丰富的问题,但对我来说似乎主要的问题是:
为什么(?<=(?(?=go)good|bad)\s)morning
失败了#34;早上好&#34;
因为你说&#34;我的问题是这个预期还是某种错误?&#34;这是我要解决的问题。
简而言之,在字符串中的某个位置x,你想说:
(?=go)
然后立即
匹配&#34;早上&#34;。显然这是不相容的。如果你有
&#34; gorning&#34;,你将有机会。紧随其后的是不可能的
两者都是&#34; mo&#34;和&#34;去&#34;。该分支中有更多语法,但这就是它失败的地方。因此正则表达式无法匹配&#34;早上好&#34;。 QED。
现在让我们看看几乎相同表达的正确程序。它成功匹配&#34; goodgopher&#34;和#34; badphilosopher&#34;。一旦你理解为什么这有效,你就会明白为什么对方没有。
using System;
using System.Text.RegularExpressions;
class Program {
static void Main() {
// Simplest
string s1 = "badphilosopher";
string s2 = "goodgopher";
string s3 = "badgopher";
string pattern = @"(?<=(?(?=go)good|bad))\w+pher";
Console.WriteLine(Regex.IsMatch(s1, pattern) );
Console.WriteLine(Regex.IsMatch(s2, pattern));
Console.WriteLine(Regex.IsMatch(s3, pattern));
Console.WriteLine("\nPress Any Key to Exit.");
Console.ReadKey();
} // END Main
} // END Program
输出:
True
True
False
关键是理解一个外观断言:&#34;在字符串中的这个确切位置,我被跟随(或前面)......&#34;
评估时,外观在字符串的一部分中牢牢固定。如果你在一行中串起几个外观,那么这个位置就不会跳转。
那么上面的正则表达式是如何工作的?
在字符串中的当前位置,我们声明:
If at this position in the string, if we are preceded by x
case 1: if at this position in the string what follows is "go"
then x is good
case 2: else
then x is bad
Then match any number of word characters followed by philosopher
为了清楚地了解前瞻和后瞻,您可能需要阅读此页面关于regex lookarounds。
评论中的子问题:
(?<=(?(?=go)good|bad))\s+\w+pher
匹配&#34;糟糕的哲学家&#34;并且&#34;坏的gopher&#34;。为什么?
这就是原因。在任何一个&#34;坏哲学家&#34;或者&#34;糟糕的地鼠&#34;,把自己放在&#34; d&#34;之后。这是你的位置x。在这个位置,
\s+\w+pher
,因为这正是您面前的(?=go)
情况,而是在其他情况下,你必须断言&#34;紧接在位置x之前的是&#34;坏&#34;。真的吗?是。但......发动机真正做了什么?
到目前为止,我还没能说服OP关于正则表达式引擎所采用的路径,所以我想我会粘贴一条痕迹。遗憾的是,我没有.NET正则表达式的跟踪工具,但我确实有一个用于PCRE的跟踪工具,一个同样强大的正则表达式引擎。
跟踪显示了pcretest报告的引擎路径,用于三个测试字符串。请注意上面提供的.NET代码中与正则表达式的一个细微差别:我们good|bad
代替goo|bad
以适应PCRE缺乏对可变长度负面外观的支持。
跟踪清楚地显示PCRE从左到右评估正则表达式:首先是lookbehind,然后是\w+pher
我无法100%确定.NET以同样的方式进行,但这肯定是正则表达式引擎的标准。当然,支持可变长度外观的引擎有可能以不同的方式进行。
PCRE version 8.34 2013-12-15
~(?<=(?(?=go)goo|bad))\w+pher~C
badphilosopher
--->badphilosopher
+0 ^ (?<=(?(?=go)goo|bad))
+0 ^ (?<=(?(?=go)goo|bad))
+0 ^ (?<=(?(?=go)goo|bad))
+0 ^ (?<=(?(?=go)goo|bad))
+4 ^ ^(?(?=go)goo|bad)
+6 ^ ^(?=go)
+9 ^ ^g
+16 ^ ^b
+17 ^ ^a
+18 ^ ^d
+19 ^ )
+20 ^ )
+21 ^ \w+
+24 ^ ^ p
+24 ^ ^ p
+24 ^ ^ p
+24 ^ ^ p
+24 ^ ^ p
+25 ^ ^ h
+26 ^ ^ e
+27 ^ ^ r
+28 ^ ^
0: philosopher
googopher
--->googopher
+0 ^ (?<=(?(?=go)goo|bad))
+0 ^ (?<=(?(?=go)goo|bad))
+0 ^ (?<=(?(?=go)goo|bad))
+0 ^ (?<=(?(?=go)goo|bad))
+4 ^ ^(?(?=go)goo|bad)
+6 ^ ^(?=go)
+9 ^ ^g
+10 ^ ^o
+11 ^ ^)
+12 ^ ^g
+13 ^ ^o
+14 ^ ^o
+15 ^ |
+20 ^ )
+21 ^ \w+
+24 ^ ^ p
+24 ^ ^ p
+24 ^ ^ p
+24 ^ ^ p
+24 ^ ^ p
+25 ^ ^ h
+26 ^ ^ e
+27 ^ ^ r
+28 ^ ^
0: gopher
badgopher
--->badgopher
+0 ^ (?<=(?(?=go)goo|bad))
+0 ^ (?<=(?(?=go)goo|bad))
+0 ^ (?<=(?(?=go)goo|bad))
+0 ^ (?<=(?(?=go)goo|bad))
+4 ^ ^(?(?=go)goo|bad)
+6 ^ ^(?=go)
+9 ^ ^g
+16 ^ ^b
+17 ^ ^a
+18 ^ ^d
+19 ^ )
+20 ^ )
+21 ^ \w+
+24 ^ ^ p
+24 ^ ^ p
+24 ^ ^ p
+24 ^ ^ p
+24 ^ ^ p
+25 ^ ^ h
+26 ^ ^ e
+27 ^ ^ r
+28 ^ ^
0: gopher
答案 1 :(得分:0)
您可以尝试这样的事情:
(?<=(?:(?=\S*\d)[\p{P}\d]+|[A-Z0-9]+)\s)matching text
答案 2 :(得分:0)
我们可以从\s+matching\s+text
开始。
要消除and matching text
和类似的匹配,我们可以添加负面的lookbehind:(?<!^\s*[A-Z]{3,})
。
为了包括其他人的匹配,我们可以使用积极的lookbehind:
匹配2个字母的模式:[A-Z\d]{1,2}
匹配更长的模式:[-A-Z\d/"']{3,}
&lt; = important,我们删除了仅由具有负后看的字母组成的字符串。
将2种模式组合在一起,我们得到以下结果:(?<=^\s*([A-Z\d]{1,2}|[-A-Z\d/"']{3,}))
完整正则表达式将是:
(?<!^\s*[A-Z]{3,})(?<=^\s*([A-Z\d]{1,2}|[-A-Z\d/"']{3,}))\s+matching\s+text
答案 3 :(得分:0)
我认为这里不需要任何外观。只需正常匹配整个字符串并使用捕获组来提取您感兴趣的部分:
Regex r = new Regex(
@"(?imn)^([A-Z0-9]{1,2}\s+|(?![A-Z]+\s)[0-9A-Z/""'-]{3,}\s+)(?<captured>matching\s+text)$");
resultString = r.Match(s).Groups["captured"].Value;
答案 4 :(得分:-3)
我想我现在明白了这个问题。 lookbehind的条件内部和不在lookbehind中的条件之间的重要区别是条件执行的时间(或者搜索光标在那个点的时间。
没有lookbehind的例子:
(?(?=go)good|bad)\smorning
早上好
条件在搜索开始时运行。所以搜索光标位于&#39;之前。在&#39; good&#39;。
good morning
^
所以此时前瞻评估为TRUE,因为它看到&#39; go&#39;
上的匹配在具有lookbehind的示例中,光标位于不同的位置。
(?<=(?(?=go)good|bad)\s)morning
搜索光标找到文本中的第一个必需项目:&#39; morning&#39;
good morning
^
实际的搜索光标留在原处消费早上&#39;如果是背后的比赛。 lookbehind将使用自己的光标来验证早上之前的情况。确定这个早上&#39;是一场比赛。后卫表示有一个&#39; \ s&#39;直到早上&#39;确实存在。临时lookbehind游标移动到空间:
good morning
^^
现在它进入条件并在条件语句中运行前瞻。在这一点上,先行者正在寻找“去”。但它看到&#39;上午&#39 ;.所以条件失败了。表达方式表示要尝试匹配“坏”&#39; (或从后面的光标向后轻拍)但它看起来很好(或从后面的光标向后推)。所以没有匹配早上&#39;。
由于条件是在感兴趣的词的末尾运行时它处于一个后视(而不是在一个外观后面的那个词的开头),所以秘密是将条件反转为一个后视而不是一个先行:
(?<=(?(?<=good)good|bad)\s)morning
这实际上匹配早晨。在这个例子中看起来没什么意义来寻找一个单词然后匹配它 - 但它说明了这个概念。在问题中陈述的现实案例中,解决方案如下所示:
(?<=(?(?<=\s\S{1,2})[A-Z0-9]+|(?![A-Z]+\s)[0-9-A-Z/"']+))\s+matching\stext
在匹配文本之前查找单词。条件检查以查看它是一个还是两个字符的单词。如果是这样,它应该只包含字母和数字。如果没有,它不仅必须由字母组成。如果满足,则可以包含字母,数字,短划线,斜线,引号和撇号。
改变了两件事:
以下是对原始问题的更多分析。享受!
@ zx81有一个实际上更好地说明正在发生的事情的例子。这个例子有更多的光标移动,所以它确实有助于说明发生了什么:
(?<=(?(?=go)good|bad))\w+pher
badphilosopher <-- 'philosopher' matches
goodgopher <-- 'gopher' matches
badgopher <-- no match
这个例子有很大的不同,因为使用了\ w +。因此,正则表达式引擎会立即匹配每个示例中的所有文本,因为该短语没有空格并且以“pher”结尾。
所以对于&#39; badphilosopher&#39;:
运行Lookbehind并立即运行条件查找&#39; go&#39;但发现&#39; ba&#39;
badphilosopher
^
条件失败,所以它尝试将错误匹配到光标左侧,但是我们位于短语的开头,所以没有匹配。
它再次检查这两个光标点,因为&#39; \ w + pher&#39;每次匹配:
badphilosopher
^
但是后卫看到&#39; b&#39;然后&#39; ba&#39;
当光标到达:
badphilosopher
^
该条件再次未能找到“去”。 (它看到&#39; ph&#39;)所以它试图匹配“坏”&#39;在光标的左边找到它!因此,\ w + pher与哲学家文本匹配。
goodgopher
^
除了条件成功之外,goodgopher以类似的方式匹配。
badgopher
^
badgopher不匹配,因为条件是成功的,但是很好的&#39;找不到光标的左侧。
在那里放置一个空格确实会改变,因为/ w + pher不再匹配整个字符串。
(?<=(?(?=go)good|bad)\s+)\w+pher
bad philosopher <-- matches philosopher
good gopher <-- no match
bad gopher <-- matches gopher
在这种情况下,光标在字符串中移动,直到它匹配\ w + pher:
bad philosopher
^
此时它开始处理后视 - 并看到&#39; \ s +&#39;在搜索光标的左侧需要它 - 它找到它并将临时lookbehind光标移动到。
bad philosopher
^^
条件现在正在运行,并且看到了&#39; go&#39;在临时看后面的光标但发现&#39; P&#39 ;.失败意味着尝试将坏匹配临时lookbehind游标的左侧,并且确实在那里找到它。
good gopher
^^
好地鼠&#39;示例进入条件并看到&#39;克&#39;所以它失败了然后寻找“坏”&#39;在光标的左侧,并没有找到它。所以这失败了。
bad philosopher
^^
同样,糟糕的哲学家&#39;到了conditonal并发现&#39; P&#39;并寻找“坏”&#39;在光标的左侧找到它。所以它匹配。
在没有lookbehind的情况下运行时,所有这些示例都匹配。这可能是违反直觉的 - 但你必须在后面考虑光标的位置。