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

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

这就是我提出的:

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

我想要一个带有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 ,或者我错了?

关于外观支持

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

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

参考

  • regular-expressions.info/Flavor比较

python

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

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

参考

  • regular-expressions.info/Lookarounds,Character classes , Alternation , Capturing groups

Java的

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字符串,这样您就不必转义\

为了完整性,以下是您在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 

相关问题

  • 我如何匹配,但排除正则表达式模式?

除非有一个特定的理由使用在问题中没有注明的lookbehind,如何简单地匹配整个事物而只捕获你感兴趣的位呢?

JavaScript示例:

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

引用regular-expressions.info :

坏消息是,大多数正则表达式都不允许您在lookbehind中使用任何正则表达式,因为它们无法向后应用正则表达式。 因此,正则表达式引擎需要能够在检查lookbehind之前找出退回的步骤数。

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

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

除了@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) 

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

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