难以理解正则表达式回溯

时间:2018-07-10 16:41:55

标签: regex regex-lookarounds

遇到此问题时,我正在浏览正则表达式标记的问题,

需要一个用于网址的正则表达式,该网址以domain.com/advertorials /

开头

正则表达式应符合以下情况,

  
      
  • domain.com/advertorials
  •   
  • domain.com/advertorials?test=true
  •   
  • domain.com/advertorials /
  •   
  • domain.com/advertorials/?test=true

         

    但不是这个

  •   
  • domain.com/advertorials/version1?test=true

  •   

我想到了此正则表达式advertorials\/?(?:(?!version)(.*))

这应该有效,但在最后一种情况下无效。查看regex101.com中的调试器, 我看到匹配“ s /”后,它逐字符匹配“版本”字词并最终匹配,但是由于这是负向的,因此条件失败。这是我无法理解的部分,因为它无法回溯到“ s /”中的“ /”之前而不是“ s /”之后。

这是应该如何工作的吗?谁能帮我理解? (这是演示链接:https://regex101.com/r/ww3HR8/1)。

谢谢

注意:人们已经对这个问题给出了解决方案,我只想知道为什么我的正则表达式会失败。

3 个答案:

答案 0 :(得分:1)

正如您已经指出的,回溯机制负责这种现象。

?量词(匹配量化子模式的1或0个重复)使正则表达式引擎通过两种方式匹配字符串:要么匹配量化子模式,要么继续将字符串与后续子模式匹配。

因此,advertorials/?(?!version)(.*)(我删除了冗余的(?:...)非捕获组),当应用于domain.com/advertorials/version1?test=true时,先匹配advertorials,然后再匹配/,然后,否定的超前查询将检查当前位置右侧是否有version子字符串。由于version之后是/,因此正则表达式引擎返回并看到/?模式可以匹配空字符串。因此,在advertorials之后重新应用超前检查。 version之后没有advertorials,并返回匹配项。

通常的解决方案是使用所有格量词或原子团,但还有其他方法。

例如

advertorials\/?+(?!version)(.*)
              ^^ 

请参见regex demo。在这里,\/?+匹配1或0个/字符,但是一旦匹配,egine就不能返回并使用此模式重新匹配字符串的一部分。

或者,您可以将/?放在前行中,并将其放在/?模式之前:

advertorials(?!\/?version)\/?(.*)

请参见another regex demo

如果您打算在使用version之后禁止在任何地方使用advertorials

advertorials(?!.*version)\/?(.*)

请参见yet another demo

答案 1 :(得分:1)

使斜杠为可选意味着有一种匹配方式而不会违反约束。如果有匹配的方法,则正则表达式引擎将始终找到它。

当斜杠后面紧跟任何东西时,使其为非可选。

function initMap() {
    var mapOptions = {
        center: new google.maps.LatLng(0, 0),
        zoom: 1,
        minZoom: 1
    };
    map = new google.maps.Map(document.getElementById('officeMap'), mapOptions);
    google.maps.event.addListenerOnce(map, 'idle', function() {
        //Map is ready
        worldViewFit(map);
    });
}
function worldViewFit(mapObj) {
    var worldBounds = new google.maps.LatLngBounds(
        new google.maps.LatLng(70.4043,-143.5291),  //Top-left
        new google.maps.LatLng(-46.11251, 163.4288)  //Bottom-right
    );
    mapObj.fitBounds(worldBounds, 0);
    var actualBounds = mapObj.getBounds();
    if(actualBounds.getSouthWest().lng() == -180 && actualBounds.getNorthEast().lng() == 180) {
        mapObj.setZoom(mapObj.getZoom()+1);
    }
}

顺便说一句,正则表达式本身不需要反斜杠转义(尽管某些宿主语言使用斜杠作为正则表达式定界符,所以也许您需要放回斜杠)。我还删除了一些多余的括号。

答案 2 :(得分:0)

原因:

此突出显示的部分是可选的 广告商\/?(?:( ?! version)(。*))

因此它也可以是advertorials(?:(?!version)(.*))
匹配advertorials/version

本质上,(?!version)(.*)/version匹配

顺便说一句,这是按1个字符进行的正常回溯。
如果您已经修复它,那么我们就完成了!