在Java中使用正则表达式匹配一个字符串多次

5

我遇到了一些问题,无法让以下正则表达式起作用。我想要匹配以下字符串:

"Please enter your name here"

导致一个包含以下元素的数组:
'please enter', 'enter your', 'your name', 'name here'

目前,我正在使用以下模式,然后创建一个匹配器,并以以下方式进行迭代:

Pattern word = Pattern.compile("[\w]+ [\w]+");
Matcher m = word.matcher("Please enter your name here");

while (m.find()) {
    wordList.add(m.group());
}

但是我得到的结果是:
'please enter', 'your name'

我做错了什么?(附注:我在regexpal.com上检查了同样的正则表达式,问题依然存在)。似乎同一个单词不会匹配两次。我该怎么做才能获得想要的结果?

谢谢。

---------------------------------

编辑: 感谢所有建议! 最终我做了这个(因为它增加了灵活性,可以轻松指定“n-gram”的数量):

Integer nGrams = 2;
String patternTpl = "\\b[\\w']+\\b";
String concatString = "what is your age? please enter your name."
for (int i = 0; i < nGrams; i++) {
    // Create pattern.
    String pattern = patternTpl;
    for (int j = 0; j < i; j++) {
        pattern = pattern + " " + patternTpl;
    }
    pattern = "(?=(" + pattern + "))";
    Pattern word = Pattern.compile(pattern);
    Matcher m = word.matcher(concatString);

    // Iterate over all words and populate wordList
    while (m.find()) {
        wordList.add(m.group(1));
    }
}

这会导致:
Pattern: 
(?=(\b[\w']+\b)) // In the first iteration
(?=(\b[\w']+\b \b[\w']+\b)) // In the second iteration

Array:
[what, is, your, age, please, enter, your, name, what is, is your, your age, please enter, enter your, your name]

注意:本文的模式来自以下最佳答案:Java regex skipping matches

将字符串用空格分割,你会得到一个单词数组,你可以获取element[i]和element[i+1]。当然,要小心OutOfBoundEx。 - Kent
4个回答

8

这些匹配项不能重叠,这就解释了你的结果。这里有一个潜在的解决方法,利用捕获组正向先行断言

Pattern word = Pattern.compile("(\\w+)(?=(\\s\\w+))");
Matcher m = word.matcher("Please enter your name here");

while (m.find()) {
    System.out.println(m.group(1) + m.group(2));
}

请在此输入您的姓名。

谢谢,这是我想做的最接近的方式,这可能比我最终所做的编辑更有效率。我会看看是否可以使用这个方法来代替我最终采取的方法。 - foglerek
我该如何使其适用于任意数量的“n-grams”?因此,如果我还想匹配“请输入您的”,“输入您的姓名”,“在此处输入您的姓名”?编辑:我已经弄清楚了,我只需根据所需的n-gram数量添加更多(?=(\s\w+))即可。 - foglerek

1
如果你想避免使用这样特定的正则表达式,也许你应该尝试一个更简单、更容易的解决方案:
public static String[] array(final String string){
    final String[] words = string.split(" ");
    final String[] array = new String[words.length-1];
    for(int i = 0; i < words.length-1; i++)
        array[i] = String.format("%s %s", words[i], words[i+1]);
    return array;
}

public static void main(String args[]){
    final String[] array = array("Please enter your name here");
    System.out.println(Arrays.toString(array));
}

输出结果为:

[请输入,输入您的,您的姓名,姓名在此]


我可能也会考虑使用这个解决方案,它肯定比我现在为 nGrams > 1 循环遍历所有单词更有效率。 - foglerek

0

类似这样:

Pattern word = Pattern.compile("(\\w+) ?");
Matcher m = word.matcher("Please enter your name here");

String previous = null;
while (m.find()) {
    if (previous != null)
        wordList.add(previous + m.group(1));
    previous = m.group();
}

模式以可选的空格结尾(如果字符串中有更多空格,则匹配)。m.group()返回整个匹配项,包括空格;m.group(1)只返回单词,不包括空格。

0

您没有做错任何事情。这只是正则表达式的工作方式(否则匹配将成为O(n^2),因为正则表达式匹配是以线性时间完成的,无法处理此类情况)。

在这种情况下,您可以简单地搜索 [\w]+ 。然后对这些组进行后期处理。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接