3

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

这就是我想出的:

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

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

这不适用于许多组合,我已经测试过10/10/1011/12/13等等......

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

但是[\d]{1,2}如果匹配\d\d也应该匹配,还是我错了?

4

4 回答 4

13

关于后视支持

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

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

参考


Python

在 Python 中,仅支持固定长度的lookbehind,您的原始模式会引发错误,因为\d{1,2}显然没有固定长度。您可以通过交替使用两个不同的固定长度后视来“解决”这个问题,例如:

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

或者,也许您可​​以将两个后视作为非捕获组的替代项:

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

(请注意,您可以\d不使用括号直接使用)。

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

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

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

此代码段说明了上述所有要点:

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}在原始模式中使用like。下面的代码片段证明了这一点:

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

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

相关问题

于 2010-07-01T16:05:32.677 回答
4

除非有使用问题中未指出的后向的特定原因,否则如何简单地匹配整个事物并仅捕获您感兴趣的位?

JavaScript 示例:

>>> /^\d{1,2}\/(\d{1,2})\/\d{1,2}$/.exec("12/12/12")[1]
"12"
于 2010-07-01T16:05:48.527 回答
3

引用正则表达式.info

坏消息是大多数正则表达式风格不允许您在后视中使用任何正则表达式,因为它们不能向后应用正则表达式。因此,正则表达式引擎需要能够计算出在检查lookbehind之前要退后多少步。

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

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

于 2010-07-01T16:02:06.710 回答
2

除了@polygenelubricants 列出的那些之外,“仅固定长度”规则还有两个例外。在 PCRE(PHP、Apache的正则表达式引擎)和 Oniguruma(Ruby 1.9、Textmate)中,后视可能由交替组成,其中每个备选可以匹配不同数量的字符,只要每个备选的长度是固定的。例如:

(?<=\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 后视一样,没有任何限制;任何可以出现在正常正则表达式中的东西都可以在\K.

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

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

编辑:差点忘了 JGSoft,EditPad Pro 和 PowerGrep 使用的正则表达式;与 .NET 一样,它具有完全不受限制的后视,正面和负面。

于 2010-07-05T01:19:26.507 回答