这很奇怪,因为它是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));
我无法理解为什么。你能帮我吗?
答案 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
)并在编译的正则表达式中简单地交换sDay
和sMonth
,您将会实际上注意到month
会正确匹配两个数字而day
会失败!
解决问题的一种方法是首先匹配双字符规则,然后然后匹配可选的单字符,就像我的回答所示。另一种方法是假设/要求您的输入日期本身就在一条线上(即日期从该行的开头开始,到该行的结尾而没有其他文本)。如果是这样,您可以使用正则表达式的^
和$
字符分别匹配行的开头和结尾:
Pattern.compile("^(" + sDay + ")" + "(?:" + sSeparators + ")" + "(" + sMonth+ ")$");
这样做,它会完全评估每个模式以找到完全匹配,在这种情况下,您应该始终匹配正确的月/日。
站点注释(建议,但不是特定答案)
根据@MarkoTopolnik的有用评论/建议,您不需要在每个组(数月+天)周围使用非捕获组,尤其是因为您立即将它们包装在捕获组中,使非捕获组无效。因此,上述模式可能只会变成:
1[0-2]|0?[1-9]