代码之家  ›  专栏  ›  技术社区  ›  Marks

为什么lookback中的有限重复在某些方面不起作用?

  •  3
  • Marks  · 技术社区  · 16 年前

    我想解析中日期中间的两个数字 dd/mm/yy 格式,但也允许日和月的个位数。

    这就是我想到的:

    (?<=^[\d]{1,2}\/)[\d]{1,2}
    

    我想要一个1或2位数的数字 [\d]{1,2} ^[\d]{1,2}\/ 在它之前。

    这对很多组合都不起作用,我已经测试过了 10/10/10 , 11/12/13

    但令我惊讶的是 (?<=^\d\d\/)[\d]{1,2} 工作。

    [\d]{1,2} 也应该匹配,如果 \d\d

    4 回复  |  直到 11 年前
        1
  •  13
  •   Community Mohan Dere    9 年前

    关于后视支持

    主要的regex风格对lookback有不同的支持;有些规定了某些限制,有些甚至根本不支持。

    • Javascript:不支持
    • Java:仅限有限长度

    工具书类


    蟒蛇

    \d{1,2} 显然没有固定的长度。您可以通过交替使用两个不同的固定长度lookbehind来“修复”此问题,例如:

    (?<=^\d\/)\d{1,2}|(?<=^\d\d\/)\d{1,2}
    

    或者,您可以将两个lookbehind作为非捕获组的替代:

    (?:(?<=^\d\/)|(?<=^\d\d\/))\d{1,2}
    

    (请注意,您可以使用 \d 没有括号)。

    也就是说,使用捕获组可能要简单得多:

    ^\d{1,2}\/(\d{1,2})
    

    请注意 findall 如果只有一个组,则返回组1捕获的内容。捕获组比lookback得到更广泛的支持,并且常常导致更可读的模式(例如在本例中)。

    此片段演示了以上所有要点:

    p = re.compile(r'(?:(?<=^\d\/)|(?<=^\d\d\/))\d{1,2}')
    
    print(p.findall("12/34/56"))   # "[34]"
    print(p.findall("1/23/45"))    # "[23]"
    
    p = re.compile(r'^\d{1,2}\/(\d{1,2})')
    
    print(p.findall("12/34/56"))   # "[34]"
    print(p.findall("1/23/45"))    # "[23]"
    
    p = re.compile(r'(?<=^\d{1,2}\/)\d{1,2}')
    # raise error("look-behind requires fixed-width pattern")
    


    Java只支持有限长度的lookbehind,因此您可以使用 \d{1,2}

        String text =
            "12/34/56 date\n" +
            "1/23/45 another date\n";
    
        Pattern p = Pattern.compile("(?m)(?<=^\\d{1,2}/)\\d{1,2}");
        Matcher m = p.matcher(text);
        while (m.find()) {
            System.out.println(m.group());
        } // "34", "23"
    

    (?m) 是嵌入式的吗 Pattern.MULTILINE 以便 ^ 匹配每行的开头。还要注意的是 \ 是字符串文字的转义字符,必须写入 "\\" 在Java中得到一个反斜杠。


    C-夏普

    C#支持lookback上的完整regex。下面的代码片段显示了如何使用 + 回头看:

    var text = @"
    1/23/45
    12/34/56
    123/45/67
    1234/56/78
    ";
    
    Regex r = new Regex(@"(?m)(?<=^\d+/)\d{1,2}");
    foreach (Match m in r.Matches(text)) {
      Console.WriteLine(m);
    } // "23", "34", "45", "56"
    

    @-quoted string 这样你就不用逃跑了 \ .

    为了完整起见,下面是如何使用C#中的捕获组选项:

    Regex r = new Regex(@"(?m)^\d+/(\d{1,2})");
    foreach (Match m in r.Matches(text)) {
      Console.WriteLine("Matched [" + m + "]; month = " + m.Groups[1]);
    }
    

    鉴于之前的 text ,此打印:

    Matched [1/23]; month = 23
    Matched [12/34]; month = 34
    Matched [123/45]; month = 45
    Matched [1234/56]; month = 56
    

    相关问题

        2
  •  4
  •   Jonny Buchanan    16 年前

    除非使用lookback有一个特定的原因,而这个原因在问题中没有提到,那么简单地匹配整个东西,只捕获你感兴趣的部分怎么样?

    JavaScript示例:

    >>> /^\d{1,2}\/(\d{1,2})\/\d{1,2}$/.exec("12/12/12")[1]
    "12"
    
        3
  •  3
  •   sepp2k    16 年前

    引用 regular-expressions.info :

    坏消息是大多数正则表达式 所有的正则表达式都在后面,因为 它们不能应用正则表达式 向后的。因此 表达式引擎需要能够 计算出要后退多少步 在检查后方之前。

    因此,许多正则表达式的味道, Python,只允许固定长度 比赛的长度是多少 预定的。这意味着你可以使用 不能使用重复或可选 只有在备选方案中的所有选项

    换句话说,您的正则表达式不起作用,因为您在lookback中使用了一个可变宽度表达式,而您的正则表达式引擎不支持这一点。

        4
  •  2
  •   Alan Moore Chris Ballance    16 年前

    (?<=\b\d\d/|\b\d/)\d{1,2}(?=/\d{2}\b)
    

    注意,替换必须在lookbehind子表达式的顶层。你可能会像我一样,想找出一些共同的因素,比如:

    (?<=\b(?:\d\d/|\d)/)\d{1,2}(?=/\d{2}\b)
    

    第二个例外更有用: \K ,由Perl和PCRE支持。它实际上意味着“假装比赛真的从这里开始。”任何出现在正则表达式之前的东西都被视为一个积极的回顾。与.NET lookbehinds一样,没有限制;在正常正则表达式中出现的任何东西都可以在 \K公司

    \b\d{1,2}/\K\d{1,2}(?=/\d{2}\b)
    

    但大多数时候,当有人对lookbehinds有问题时,他们甚至不应该使用它们。正如@insin所指出的,通过使用捕获组可以更容易地解决这个问题。

    编辑: