仅左侧的正则表达式贪婪(.net)

Jos*_*osh 1 .net c# regex non-greedy regex-greedy

我正在尝试捕获两个字符串之间的匹配项。

例如,我正在寻找出现在 Q 和 XYZ 之间的所有文本,使用“最快”匹配(不继续向外扩展)。这个字符串:

马戏团 Q 你好Q SOMETEXT XYZ今天是 XYZ 的一天 XYZ

应该返回:

Q 某些文本 XYZ

但相反,它返回:

Q 你好 Q SOMETEXT XYZ

这是我使用的表达式: Q.*?XYZ

回到左边太远了。当我在星号后面使用问号时,它在骑行方面工作正常。我怎样才能对左侧做同样的事情,并在我击中第一个左 Q 后停止,使其与右侧的工作相同?我已经尝试过http://msdn.microsoft.com/en-us/library/az24scfc.aspx 中的问号和其他符号,但有些东西我只是想不通。

我是一个正则表达式新手,因此对这方面的任何帮助将不胜感激!

Fra*_*Man 5

好吧,非贪婪匹配正在起作用 - 它获取满足正则表达式的最短字符串。您必须记住的是,正则表达式是一个从左到右的过程。所以它匹配第一个 Q,然后获取最短的字符数,然后是 XYZ。如果您希望它不超过任何 Q,则必须使用否定字符类:

Q[^Q]*?XYZ
Run Code Online (Sandbox Code Playgroud)

[^Q] 匹配任何一个不是 Q 的字符。请注意,这仅适用于单个字符。如果您的开始分隔符是多个字符,则必须采用不同的方式。为什么?好吧,取定界符“PQR”,字符串是

foo PQR bar XYZ 
Run Code Online (Sandbox Code Playgroud)

如果您尝试使用之前的正则表达式,但您将字符类扩展为:

PQR[^PQR]*?XYZ
Run Code Online (Sandbox Code Playgroud)

然后你会得到

'PQR bar XYZ'
Run Code Online (Sandbox Code Playgroud)

正如你所料。但是如果你的字符串是

foo PQR Party Time! XYZ 
Run Code Online (Sandbox Code Playgroud)

你不会得到匹配。这是因为 [] 描述了一个“字符类”——它正好匹配一个字符。使用这些类,您可以匹配一系列字符,只需列出它们即可。

th[ae]n
Run Code Online (Sandbox Code Playgroud)

将匹配 'than' 和 'then',但不匹配 'thin'。在开头放置克拉 ('^') 会否定类 - 意思是“匹配除这些字符之外的任何内容” - 因此,通过将我们的单字符分隔符转换为 [^PQR],而不是说“不是 'PQR'”,您'重新说“不是‘P’、‘Q’或‘R’”。如果您愿意,您仍然可以使用它,但前提是您 100% 确定分隔符中的字符只会在您的分隔符中。如果是这种情况,使用贪婪匹配并仅否定分隔符的第一个字符会更快。正则表达式为:

PQR[^P]*XYZ 
Run Code Online (Sandbox Code Playgroud)

但是,如果你不能保证,那么匹配:

PQR(?:.(?!PQR))*?XYZ
Run Code Online (Sandbox Code Playgroud)

正则表达式不直接支持负字符串匹配(因为它无法定义,当您考虑时),因此您必须使用负前瞻

(?!PQR)
Run Code Online (Sandbox Code Playgroud)

就是这样一个超前。它的意思是“断言接下来的几个字符不是这个内部正则表达式”,没有匹配任何字符,所以

.(?!PQR)
Run Code Online (Sandbox Code Playgroud)

匹配后面没有 PQR 的任何字符。把它包成一个组,这样你就可以懒洋洋地重复一遍,

(.(?!PQR))*?
Run Code Online (Sandbox Code Playgroud)

并且您匹配了“不包含我的分隔符的字符串”。我所做的唯一一件事就是添加一个 ?: 以使其成为非捕获组。

(?:.(?!PQR))*?
Run Code Online (Sandbox Code Playgroud)

根据您用于解析正则表达式的语言,它可能会尝试单独传回每个匹配的组(对于查找和替换很有用)。这可以防止它这样做。

快乐的正则表达式!