如果模式的首次出现与条件不匹配,如何使匹配失败?

时间:2018-10-28 20:38:31

标签: regex

我有key=value对这样的逗号分隔字符串:

foo=1,foo=1,bar=2

在此字符串中,我要捕获 first foo的值,但前提是紧随其后的是bar=2

示例:

  • 在此字符串中,应捕获值1

    baz=0,foo=1,bar=2,foo=3,bar=4
    
  • 在此字符串中,不应捕获任何内容:

    baz=0,foo=1,foo=1,bar=2
    

我当前的解决方案使用了经过调节的贪婪令牌,但这迫使我复制了正则表达式的foo=[^,]*,部分:

^(?:(?!foo=[^,]*,).)*foo=([^,]*),bar=2(?:,|$)

有什么方法可以做到而不必复制正则表达式的很大一部分?

2 个答案:

答案 0 :(得分:3)

使用backtracking control verbs很简单:

(?<![^,])foo=([^,]*)(*COMMIT),bar=2(?![^,])

我们匹配一个不以非逗号字符开头的位置(例如,字符串的开头或紧接,之后),然后是foo=,然后是0个或多个非逗号字符(我们捕获)。这是foo=...部分。

然后我们提交找到的第一个匹配项,并要求进行,bar=2匹配,而不是非逗号字符(即,或字符串的结尾)。

答案 1 :(得分:0)

免责声明:仅在某些正则表达式引擎中有效。

某些正则表达式引擎具有我们可以滥用的“特征”:提前捕获组具有所有格;一旦匹配,就无法再更改其值。

利用此“功能”,可以将正则表达式编写为:

.*?(?!\1)thing_you_want_the_first_occurrence_of(?=())rest_of_the_regex

在这种特定情况下,看起来像这样(由于foo=([^,]*)包含捕获组,捕获组的索引移动了1):

.*?(?!\2)(?<![^,])foo=([^,]*),(?=())bar=2(?![^,])

那么,它如何工作?

找到foo=的第一个匹配项后,组(?=())匹配。因为它位于先行之内,所以它永远无法再更改其值-甚至回溯也不会影响它。因此,从现在开始,(?!\2)将不再匹配。现在发现“ foo=的第一次出现这一事实已被“锁定”并且无法撤消。如果正则表达式回溯并尝试使.*?与更多文本匹配,则(?!\2)会阻止这种情况。

使用python的PyPI regex module进行演示:

>>> pattern = r'.*?(?!\2)(?<![^,])foo=([^,]*),(?=())bar=2(?![^,])'
>>> regex.match(pattern, 'baz=0,foo=1,bar=2,foo=3,bar=4').group(1)
'1'
>>> regex.match(pattern, 'baz=0,foo=1,foo=1,bar=2')
>>>