我有这个:
import java.util.regex.*;
String regex = "(?<m1>(hello|universe))|(?<m2>(hello world))";
String s = "hello world";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(s);
while(matcher.find()) {
MatchResult matchResult = m.toMatchResult();
String substring = s.substring(matchResult.start(), matchResult.end());
System.out.println(substring);
}
以上仅打印hello
,而我希望它打印hello world
。
解决此问题的一种方法是重新排序String regex = "(?<m2>(hello world))|(?<m1>(hello|universe))"
中的群组,但我无法控制我的情况下的正则表达式...
那么找到最长匹配的最佳方法是什么?一种显而易见的方法是按长度检查此处提到的s
的所有可能子串(Efficiently finding all overlapping matches for a regular expression)并选择第一个但是O(n^2)
。我们可以做得更好吗?
答案 0 :(得分:3)
这是一种使用匹配器区域的方法,但是在字符串索引上有一个循环:
public static String findLongestMatch(String regex, String s) {
Pattern pattern = Pattern.compile("(" + regex + ")$");
Matcher matcher = pattern.matcher(s);
String longest = null;
int longestLength = -1;
for (int i = s.length(); i > longestLength; i--) {
matcher.region(0, i);
if (matcher.find() && longestLength < matcher.end() - matcher.start()) {
longest = matcher.group();
longestLength = longest.length();
}
}
return longest;
}
我强制模式匹配直到区域结束,然后我将区域的末端从最右边的字符串索引向左移动。对于尝试的每个区域,Java将匹配在该区域结束处完成的最左边的起始子字符串,即在该位置结束的最长子字符串。最后,这只是跟踪到目前为止发现的最长匹配的问题。
作为优化的问题,由于我从较长的区域开始向较短的区域开始,所以当所有后来的区域已经比已经找到的最长子串的长度短时,我就会停止循环。
这种方法的一个优点是它可以处理任意正则表达式,并且不需要特定的模式结构:
findLongestMatch("(?<m1>(hello|universe))|(?<m2>(hello world))", "hello world")
==> "hello world"
findLongestMatch("hello( universe)?", "hello world")
==> "hello"
findLongestMatch("hello( world)?", "hello world")
==> "hello world"
findLongestMatch("\\w+|\\d+", "12345 abc")
==> "12345"
答案 1 :(得分:2)
如果您只处理这种特定模式:
|
连接的最高级别上有一个或多个命名组。|
连接的一个或多个文字。|
,(
或)
。然后可以通过提取文字,按长度排序然后返回第一个匹配来编写解决方案:
private static final Pattern g = Pattern.compile("\\(\\?\\<[^>]+\\>\\(([^)]+)\\)\\)");
public static final String findLongestMatch(String s, Pattern p) {
Matcher m = g.matcher(p.pattern());
List<String> literals = new ArrayList<>();
while (m.find())
Collections.addAll(literals, m.group(1).split("\\|"));
Collections.sort(literals, new Comparator<String>() {
public int compare(String a, String b) {
return Integer.compare(b.length(), a.length());
}
});
for (Iterator<String> itr = literals.iterator(); itr.hasNext();) {
String literal = itr.next();
if (s.indexOf(literal) >= 0)
return literal;
}
return null;
}
测试:
System.out.println(findLongestMatch(
"hello world",
Pattern.compile("(?<m1>(hello|universe))|(?<m2>(hello world))")
));
// output: hello world
System.out.println(findLongestMatch(
"hello universe",
Pattern.compile("(?<m1>(hello|universe))|(?<m2>(hello world))")
));
// output: universe
答案 2 :(得分:1)
只需在Or分隔符
$
之前添加|
(字符串结尾) 然后检查字符串是否结束。如果结束,它将返回字符串。否则跳过正则表达式的那部分。
以下代码提供了您想要的内容
import java.util.regex.*;
public class RegTest{
public static void main(String[] arg){
String regex = "(?<m1>(hello|universe))$|(?<m2>(hello world))";
String s = "hello world";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(s);
while(matcher.find()) {
MatchResult matchResult = matcher.toMatchResult();
String substring = s.substring(matchResult.start(), matchResult.end());
System.out.println(substring);
}
}
}
同样,以下代码将跳过 hello , hello world 并匹配 hello world
的用法
查看$
那里
import java.util.regex.*;
public class RegTest{
public static void main(String[] arg){
String regex = "(?<m1>(hello|universe))$|(?<m2>(hello world))$|(?<m3>(hello world there))";
String s = "hello world there";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(s);
while(matcher.find()) {
MatchResult matchResult = matcher.toMatchResult();
String substring = s.substring(matchResult.start(), matchResult.end());
System.out.println(substring);
}
}
}
答案 3 :(得分:1)
如果正则表达式的结构总是相同的,这应该有效:
String regex = "(?<m1>(hello|universe))|(?<m2>(hello world))";
String s = "hello world";
//split the regex into the different groups
String[] allParts = regex.split("\\|\\(\\?\\<");
for (int i=1; i<allParts.length; i++) {
allParts[i] = "(?<" + allParts[i];
}
//find the longest string
int longestSize = -1;
String longestString = null;
for (int i=0; i<allParts.length; i++) {
Pattern pattern = Pattern.compile(allParts[i]);
Matcher matcher = pattern.matcher(s);
while(matcher.find()) {
MatchResult matchResult = matcher.toMatchResult();
String substring = s.substring(matchResult.start(), matchResult.end());
if (substring.length() > longestSize) {
longestSize = substring.length();
longestString = substring;
}
}
}
System.out.println("Longest: " + longestString);