日期的正则表达式省略了月份的一个字符

时间:2012-10-25 12:40:35

标签: java regex date regex-group

这很奇怪,因为它是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));

我无法理解为什么。你能帮我吗?

1 个答案:

答案 0 :(得分:3)

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

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

应该成为:

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

更新(推理)
0?模式中以day为导向的相同模式在month模式下工作但不在day模式中的原因是因为您指定了必须遵循{的字符{1}}模式 - 因此,处理day的整个模式。但是,在month模式中,没有指定要遵循的字符;因此,它在找到第一场比赛时停止,在原始模式中,这是一个数字。

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

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

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

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

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

1[0-2]|0?[1-9]