4

这很奇怪,因为它是非常简单的dd/mm格式正则表达式。结果应该是:"Group 1: 14; Group 2: 12"但它是"Group 1: 14; Group 2: 1"

第二组只捕获了第一个字符,但省略了第二个字符(示例中为“2”)。

String sDay = "(?:0?[1-9]|[12][0-9]|3[01])";
String sMonth = "(?:0?[1-9]|1[0-2])";
String sDot = "[\\.]";
String sSlash = "[/]";
String sMinus = "[\\-]";
String sSeparators = (sDot + "|" + sSlash + "|" + sMinus);

Pattern reDayMonth =
    Pattern.compile("(" + sDay + ")" + "(?:" + sSeparators + ")" + "(" + sMonth+ ")");

String s = "14/12";
Matcher reMatcher = reDayMonth.matcher(s);
boolean found = reMatcher.find();

System.out.println("Group 1: " + reMatcher.group(1) + "; Group 2: " + reMatcher.group(2));

我不明白为什么。请你帮助我好吗?

4

1 回答 1

3

在您的月份正则表达式中,您首先允许单个数字匹配,因此它会匹配(然后停止)。尝试移动所需的两位数月份以先检查,然后再检查一位数:

(?:0?[1-9]|1[0-2])

应该变成:

(?:1[0-2]|0?[1-9])

更新 (推理)
模式中以 开头的相同模式有效但在模式中无效的0?原因是因为您指定了必须遵循该模式的字符 - 因此,处理 的整个模式。但是,在该模式中,没有指定要遵循的字符;因此,它会在找到第一个匹配项时停止,该匹配项在原始模式中是单个数字。daymonthdaydaymonth

如果您要反转输入格式(即,而不是dd/mm您使用mm/dd)并简单地交换sDaysMonth在编译的正则表达式中,您实际上会注意到month将正确匹配两个数字并且day会失败!

解决问题的一种方法是首先匹配两个字符的规则,然后匹配可选的单字符,就像我的回答所暗示的那样。另一种方法是假设/要求您的输入日期单独在一行上(即日期从行首开始,到行尾结束,没有其他文本)。如果这是真的,您可以使用正则表达式^$字符分别匹配行的开头和结尾:

Pattern.compile("^(" + sDay + ")" + "(?:" + sSeparators + ")" + "(" + sMonth+ ")$");

这样做,它将完全评估每个模式以找到完全匹配,在这种情况下,您应该始终匹配正确的月/日。

站点注释(建议,但不是针对特定答案)
根据@MarkoTopolnik 的有用评论/建议,您不需要在每个组周围使用非捕获组(月+天),特别是因为您立即将它们包装在捕获组使非捕获组无用。因此,上述模式可以简单地变成:

1[0-2]|0?[1-9]
于 2012-10-25T12:48:51.130 回答