我前一阵子输入了这个,但很忙(现在仍然很忙,所以我可能需要一段时间才能回复)并且没有时间发布它。如果您仍然愿意接受答案...
是否有一些教学资源正在推广它们?
我不这么认为,我相信这只是巧合。
但是,例如,最近一个问题的这个查询有一个简单的解决方案来捕获.*
,但是为什么要使用后面的查找呢?
(?<=<td><a href="\/xxx\.html\?n=[0-9]{0, 5}">).*(?=<\/a><span
这很可能是一个 C# 正则表达式,因为我的许多正则表达式引擎不支持可变宽度后视。好吧,这里当然可以避免环顾四周,因为为此,我相信拥有捕获组确实更简单(并让.*
我们变得懒惰):
(<td><a href="\/xxx\.html\?n=[0-9]{0,5}">).*?(<\/a><span)
如果是为了更换,或者
<td><a href="\/xxx\.html\?n=[0-9]{0,5}">(.*?)<\/a><span
比赛。尽管在这里使用 html 解析器肯定会更可取。
在这种情况下,我相信环视会更慢。请参阅regex101 演示,其中匹配是捕获组的 64 步,但环视的匹配是 94+19 = 1-3 步。
什么时候使用积极的环视真的更好?你能举一些例子吗?
好吧,lookarounds 具有零宽度断言的属性,这意味着它们不会真正有助于匹配,而它们有助于决定匹配什么并且还允许重叠匹配。
想一想,我也认为,消极的环顾四周使用得更频繁,但这并不会使积极的环顾四周变得不那么有用!
在浏览我的一些旧答案时,我可以找到一些“漏洞利用”(下面的链接将是来自 regex101 的演示)。当/如果你看到你不熟悉的东西,我可能不会在这里解释它,因为问题集中在积极的环顾四周,但你总是可以查看我提供的演示链接,其中有正则表达式的描述,如果您仍然需要一些解释,请告诉我,我会尽力解释。
要获得某些字符之间的匹配:
在某些比赛中,正向前瞻使事情变得更容易,前瞻也可以做,或者当不使用前瞻不太实际时:
狗叹了口气。“我不是超级狗,也不是特别狗,”狗说,“我是普通狗,别管我!” 狗推开他,走向另一只狗。
我们希望获得所有dog
(无论大小写)外部报价。通过积极的展望,我们可以做到这一点:
\bdog\b(?=(?:[^"]*"[^"]*")*[^"]*$)
以确保前面有偶数个报价。使用负前瞻,它看起来像这样:
\bdog\b(?!(?:[^"]*"[^"]*")*[^"]*"[^"]*$)
以确保前面没有奇数的报价。或者如果您不想要前瞻,请使用类似的东西,但您必须提取第 1 组匹配项:
(?:"[^"]+"[^"]+?)?(\bdog\b)
好的,现在说我们想要相反的;在引号内找到“狗” 。带有环视的正则表达式只需要反转符号,first和second:
\bdog\b(?!(?:[^"]*"[^"]*")*[^"]*$)
\bdog\b(?=(?:[^"]*"[^"]*")*[^"]*"[^"]*$)
但如果没有前瞻,这是不可能的。你能得到的最接近的可能是这样的:
"[^"]*(\bdog\b)[^"]*"
但这并没有得到所有的比赛,或者你可以使用这个:
"[^"]*?(\bdog\b)[^"]*?(?:(\bdog\b)[^"]*?)?"
但这对于更多的出现是不切实际的,dog
并且您会在变量中获得越来越多的数字...这确实更容易使用环视,因为它们是零宽度断言,您不必担心环视内部的表达式匹配dog
与否,否则正则表达式不会获得dog
引号中的所有出现。
当然,现在这个逻辑可以扩展到字符组,例如获取单词之间的特定模式,例如start
和end
。
重叠匹配
如果你有这样的字符串:
abcdefghijkl
并且想要提取所有可能的连续 3 个字符,您可以使用这个:
(?=(...))
如果你有类似的东西:
1A Line1 Detail1 Detail2 Detail3 2A Line2 Detail 3A Line3 Detail Detail
并且想要提取这些,知道每一行都以#A Line#
(其中#
是一个数字)开头:
1A Line1 Detail1 Detail2 Detail3
2A Line2 Detail
3A Line3 Detail Detail
你可以试试这个,因为贪婪而失败......
[0-9]+A Line[0-9]+(?: \w+)+
或者这个,当它变得懒惰时不再起作用......
[0-9]+A Line[0-9]+(?: \w+)+?
但是通过积极的展望,你会得到:
[0-9]+A Line[0-9]+(?: \w+)+?(?= [0-9]+A Line[0-9]+|$)
并适当地提取需要的东西。
另一种可能的情况是您有这样的情况:
#ff00fffirstword#445533secondword##008877thi#rdword#
您要转换为三对变量(其中第一对是 # 和一些十六进制值 (6) 以及它们后面的任何字符):
#ff00ff and firstword
#445533 and secondword#
#008877 and thi#rdword#
如果“单词”中没有散列,使用 就足够了(#[0-9a-f]{6})([^#]+)
,但不幸的是,情况并非如此,您必须求助于.*?
而不是[^#]+
,这还不能完全解决杂散散列的问题。然而,积极的前瞻使这成为可能:
(#[0-9a-f]{6})(.+?)(?=#[0-9a-f]{6}|$)
验证和格式化
不推荐,但您可以使用积极的前瞻来快速验证。例如,以下正则表达式允许输入包含至少 1 个数字和 1 个小写字母的字符串。
^(?=[^0-9]*[0-9])(?=[^a-z]*[a-z])
当您检查字符长度但在 a 字符串中具有不同长度的模式时,这可能很有用,例如,具有有效格式的 4 字符长字符串,其中#
表示数字,连字符/破折号/减号-
必须在中间:
##-#
#-##
像这样的正则表达式可以解决问题:
^(?=.{4}$)\d+-\d+
否则,您^(?:[0-9]{2}-[0-9]|[0-9]-[0-9]{2})$
现在可以想象最大长度为 15;您需要的更改次数。
如果您想要一种快速而肮脏的方式以“混乱”格式重新排列某些日期mmm-yyyy
并yyyy-mm
使用更统一的格式mmm-yyyy
,您可以使用以下方法:
(?=.*(\b\w{3}\b))(?=.*(\b\d{4}\b)).*
输入:
Oct-2013
2013-Oct
输出:
Oct-2013
Oct-2013
另一种方法可能是使用正则表达式(正常匹配)并分别处理所有不合格的格式。
我在 SO 上遇到的其他东西是印度货币格式,它是##,##,###.###
(小数点左侧 3 位数字和所有其他数字成对分组)。如果你有一个输入122123123456.764244
,你期望1,22,12,31,23,456.764244
并且如果你想使用一个正则表达式,这个是这样的:
\G\d{1,2}\K\B(?=(?:\d{2})*\d{3}(?!\d))
((?:\G|^)
仅使用链接中的 ,因为\G
仅在字符串的开头和匹配后匹配)并且我认为如果没有积极的前瞻,这将无法工作,因为它会在不移动替换点的情况下向前看。)
修剪
假设你有:
this is a sentence
并想用一个正则表达式修剪所有空格。您可能很想对空格进行一般替换:
\s+
但这会产生thisisasentence
. 好吧,也许用一个空格替换?它现在产生“这是一个句子”(使用双引号,因为反引号会吃掉空格)。但是,您可以这样做:
^\s*|\s$|\s+(?=\s)
这确保留下一个空格,以便您可以用任何内容替换并得到“这是一个句子”。
分裂
好吧,积极的环顾可能有用的其他地方是,假设你有一个字符串ABC12DE3456FGHI789
并且想要将字母+数字分开,那就是你想要得到ABC12
,DE3456
和FGHI789
。您可以轻松地使用正则表达式:
(?<=[0-9])(?=[A-Z])
而如果你使用([A-Z]+[0-9]+)
(即捕获的组被放回结果列表/数组/等中,你也会得到空元素。
请注意,这也可以通过匹配来完成,使用[A-Z]+[0-9]+
如果我不得不提到负面的环视,这篇文章会更长:)