为什么在某些风格的外观工作中没有有限的重复?

时间:2010-07-01 15:58:29

标签: c# java python regex lookbehind

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

这就是我提出的:

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

我想要一个带有1位或2位数字的1位或2位数字[\d]{1,2},并在其前面加上^[\d]{1,2}\/

这对许多组合都不起作用,我测试了10/10/1011/12/13等...

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

[\d]{1,2}如果\d\d做了,或者我错了,那么{{1}}也应匹配?

4 个答案:

答案 0 :(得分:13)

on lookbehind support

主要的正则表达式风格对不同的外观有不同的支持;有些人施加了某些限制,有些甚至根本不支持。

  • Javascript:不支持
  • Python:仅限固定长度
  • Java:仅限有限长度
  • .NET:没有限制

参考


的Python

在Python中,只支持固定长度的lookbehind,原始模式会引发错误,因为\d{1,2}显然没有固定长度。您可以通过在两个不同的固定长度的外观上交替来“修复”这个,例如,像这样的东西:

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

或许您可以将两个lookbehinds作为非捕获组的替代:

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

(请注意,您可以在没有括号的情况下使用\d

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

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

请注意,如果您只有一个组,findall将返回第1组捕获的内容。捕获组比后观更受支持,并且通常会导致更易读的模式(例如在这种情况下)。

此片段说明了以上所有要点:

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#支持lookbehind的完整正则表达式。以下代码段显示了如何在lookbehind上使用+重复:

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"

请注意,与Java不同,在C#中,您可以使用@-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

相关问题

答案 1 :(得分:4)

除非有一个特定的原因使用了问题中未提及的lookbehind,如何简单地匹配整个事物而只捕获你感兴趣的位呢?

JavaScript示例:

>>> /^\d{1,2}\/(\d{1,2})\/\d{1,2}$/.exec("12/12/12")[1]
"12"

答案 2 :(得分:3)

引用regular-expressions.info

  

坏消息是大多数正则表达式   味道不允许你只使用   在lookbehind里面的任何正则表达式,因为   他们不能应用正则表达式   向后。因此,经常   表达引擎需要能够   弄清楚要退后几步   在检查外观之前。

     

因此,许多正则表达式的味道,   包括Perl和   Python,只允许固定长度   字符串。你可以使用任何正则表达式   匹配的长度可以是   预定的。这意味着你可以使用   文字文本和字符类。   您不能使用重复或可选   项目。你可以使用交替,但是   只有在交替中的所有选项   长度相同。

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

答案 3 :(得分:2)

除了@polygenelubricants列出的那些之外,“仅限固定长度”规则还有两个例外。在PCRE(PHP的正则表达式引擎,Apache,)和Oniguruma(Ruby 1.9,Textmate)中,lookbehind可能包含一个替换,其中每个替代可能匹配不同数量的字符,如每个替代方案的长度是固定的。例如:

(?<=\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)

但大多数时候,当有人遇到外观问题时,事实证明他们甚至不应该使用它们。正如@insin指出的那样,使用捕获组可以更容易地解决这个问题。

编辑:几乎忘记了JGSoft,EditPad Pro和PowerGrep使用的正则表达式风格;像.NET一样,它具有完全不受限制的外观,正面和负面。