输入字符串:
aaa---foo---ccc---ddd
aaa---bar---ccc---ddd
aaa---------ccc---ddd
正则表达式:aaa.*(foo|bar)?.*ccc.*(ddd)
此正则表达式在任何情况下都找不到第一组(foo | bar)。对于捕获组1,它始终返回null
。
我的问题是为什么以及如何避免这种情况。
这是我的正则表达式的简单示例。如果我删除?
量词,但输入字符串可以完全没有此组(aaa---------ccc---ddd
),那么它仍然有用,我仍然需要确定它是foo
还是bar
还是{ {1}}。但是第1组始终是null
。
使用此正则表达式和测试字符串的页面:http://fiddle.re/45c766
答案 0 :(得分:3)
如果要捕获中间foo
或bar
字符串,请将正则表达式更改为以下内容。
aaa(?:(?!foo|bar).)*(foo|bar)?.*?ccc.*?(ddd)
由于.*
也会占用中间字符串foo
或bar
,因此您可以使用(?:(?!foo|bar).)*
而不是(?:(?!foo|bar).)*
。这个foo
正则表达式会匹配任何字符,但不会匹配bar
或String s = "aaa---foo---ccc---ddd\n" +
"aaa---bar---ccc---ddd\n" +
"aaa---------ccc---ddd";
Pattern regex = Pattern.compile("aaa(?:(?!foo|bar).)*(foo|bar)?.*?ccc.*?(ddd)");
Matcher matcher = regex.matcher(s);
while(matcher.find()){
System.out.println(matcher.group(1));
}
零次或多次。
foo
bar
null
<强>输出:强>
{{1}}
答案 1 :(得分:3)
这就是为什么它不起作用:当你在模式中有.*
时,匹配器的算法就是尝试匹配尽可能多的字符来制作其余的模式工作。在这种情况下,如果它尝试从字符串的整个剩余部分开始.*
并删除一个字符直到匹配,则会发现(对于"aaa---foo---ccc---ddd"
)它将有效.*
匹配9个字符;那么(foo|bar)?
并不匹配任何东西,这是可以的,因为它是可选的;并且下一个.*
匹配0个字符,然后其余模式匹配。这就是它选择的那个。
将.*
更改为.*?
的原因:
aaa.*?(foo|bar)?.*?ccc.*(ddd)
不起作用的是匹配器反向做同样的事情。它以0字符匹配开始,然后确定它是否可以使模式工作。尝试此操作时,会发现它可以使.*?
匹配0个字符;那么(foo|bar)?
并不匹配任何东西;然后第二个.*?
匹配9个字符;那么模式的其余部分匹配ccc---ddd
。无论哪种方式,它都不会做你想做的事。
答案中有几个解决方案,都涉及前瞻性。这是另一个解决方案:
aaa.*(foo|bar).*ccc.*(ddd)|aaa.*ccc.*(ddd)
这基本上按顺序检查两种模式;首先检查其中是否有foo|bar
的模式,如果匹配不匹配,则会搜索另一种可能性,而不是foo|bar
。如果有foo|bar
,我们总会找到它。
然而,所有这些解决方案都涉及相当难以阅读的正则表达式。这就是我编写它的方式:
Pattern pat1 = Pattern.compile("aaa(.*)ccc.*ddd");
Pattern pat2 = Pattern.compile("foo|bar");
Matcher m1 = pat1.matcher(source);
String foobar;
if (m1.matches()) {
Matcher m2 = pat2.matcher(m1.group(1));
if (m2.find()) {
foobar = m2.group(0);
} else {
foobar = null;
}
}
通常,尝试使用一个whiz-bang正则表达式来解决问题会导致代码不太可读(并且可能效率较低),而不仅仅是将问题分解为部分。
答案 2 :(得分:-2)
尝试:
.{3}\-{3}(.{3})\-{3}.{3}\-{3}(.{3})