在Java中如何查找字符串中的完整单词?

32

我有一个字符串需要解析不同的关键词,例如:

"我将在123woods见你"

我的关键词是

'123woods'
'woods'

每当出现匹配时,我都应该报告在哪里发生了匹配。还应该考虑到多个匹配情况。

然而,在这个例子中,只应该在 '123woods' 上匹配,而不是 'woods'。这排除了使用 String.contains() 方法。同时,我应该能够拥有关键词列表 / 集并同时检查它们的出现。例如,如果我有 '123woods''come',那么我应该会得到两个匹配。此方法在大文本上执行应该相对较快。

我的想法是使用 StringTokenizer ,但我不确定它的性能表现。有什么建议吗?


1
你确定逻辑没有问题吗?如果有关键词- words123 和 123words,那么在文本中的 words123words 是匹配的吗? - Petar Minchev
无。我只需要精确的词语匹配。 - Nikola Yovchev
14个回答

49
下面的例子是基于您的评论。它使用关键字列表,将使用单词边界在给定字符串中搜索。它使用来自Apache Commons Lang的StringUtils构建正则表达式并打印匹配的组。
String text = "I will come and meet you at the woods 123woods and all the woods";

List<String> tokens = new ArrayList<String>();
tokens.add("123woods");
tokens.add("woods");

String patternString = "\\b(" + StringUtils.join(tokens, "|") + ")\\b";
Pattern pattern = Pattern.compile(patternString);
Matcher matcher = pattern.matcher(text);

while (matcher.find()) {
    System.out.println(matcher.group(1));
}

如果您正在寻找更高的性能,可以看一下StringSearch:Java中高性能的模式匹配算法。


如果我有一个ArrayList<String>,我想使用Pattern来构建它怎么办?看起来我得使用可靠的老StringBuilder? - Nikola Yovchev
1
@baba - 你可以这样做,或者你可以遍历List<>。我不确定哪种方法更有效,如果性能是一个问题,你可能想尝试两种方法。 - user
我是说如果我有一个关键字的ArrayList来在字符串中搜索,例如,我的ArrayList<String>将包含woods、123woods和其他一些单词。我必须在迭代时使用StringBuilder来构建Pattern。然后,当Pattern找到匹配项时,我需要查找我的ArrayList以查看哪个关键字被匹配。此外,substring因其性能差而闻名,并且它会在循环中创建一个新的String对象。对我来说,似乎应该有更好的解决方案,但我想不出来。 - Nikola Yovchev
1
@baba:现在我开始明白了。我根据你的评论更新了我的答案。 - Chris
3
在Java 8中,不再需要使用StringUtilsString类有一个静态的join()方法可以完成相同的工作。 - Ahmad Shahwan
显示剩余3条评论

20

像其他人提供的一样,使用正则表达式和单词边界。

"I will come and meet you at the 123woods".matches(".*\\b123woods\\b.*");

将是真的。

"I will come and meet you at the 123woods".matches(".*\\bwoods\\b.*");

将会是 false。


12
希望这对你有用:
String string = "I will come and meet you at the 123woods";
String keyword = "123woods";

Boolean found = Arrays.asList(string.split(" ")).contains(keyword);
if(found){
      System.out.println("Keyword matched the string");
}

http://codigounico.blogspot.com/


9

4

在Android中有一种方法可以匹配字符串中的确切单词:

String full = "Hello World. How are you ?";

String one = "Hell";
String two = "Hello";
String three = "are";
String four = "ar";


boolean is1 = isContainExactWord(full, one);
boolean is2 = isContainExactWord(full, two);
boolean is3 = isContainExactWord(full, three);
boolean is4 = isContainExactWord(full, four);

Log.i("Contains Result", is1+"-"+is2+"-"+is3+"-"+is4);

Result: false-true-true-false

匹配单词的函数:

private boolean isContainExactWord(String fullString, String partWord){
    String pattern = "\\b"+partWord+"\\b";
    Pattern p=Pattern.compile(pattern);
    Matcher m=p.matcher(fullString);
    return m.find();
}

完成


3
public class FindTextInLine {
    String match = "123woods";
    String text = "I will come and meet you at the 123woods";

    public void findText () {
        if (text.contains(match)) {
            System.out.println("Keyword matched the string" );
        }
    }
}

虽然这段代码片段可能解决了问题,但包括解释真的有助于提高您的帖子质量。请记住,您正在为未来的读者回答问题,而这些人可能不知道您的代码建议原因。 - awh112

3

尝试使用正则表达式进行匹配。匹配"\b123wood\b",其中\b表示单词边界。


2
这个解决方案似乎已经被广泛接受,但是可以进行改进,因此如果有人遇到类似的问题:
这是多模式搜索算法的典型应用。
Java Pattern Search (使用Matcher.find)不适合执行该操作。在 Java 中,搜索一个关键字进行了优化,而搜索或表达式则使用正则表达式的非确定性自动机,在不匹配时回溯。在最坏的情况下,文本的每个字符都将被处理l次(其中l是模式长度的总和)。
单模式搜索更好,但也不够合格。每个关键字模式都需要重新开始整个搜索。在最坏的情况下,文本的每个字符都将被处理p次,其中p是模式的数量。
多模式搜索将精确处理文本的每个字符一次。适合这种搜索的算法有Aho-Corasick、Wu-Manber或Set Backwards Oracle Matching。这些可以在Stringsearchalgorithmsbyteseek等库中找到。
// example with StringSearchAlgorithms

AhoCorasick stringSearch = new AhoCorasick(asList("123woods", "woods"));

CharProvider text = new StringCharProvider("I will come and meet you at the woods 123woods and all the woods", 0);

StringFinder finder = stringSearch.createFinder(text);

List<StringMatch> all = finder.findAll();

1
一个更简单的方法是使用 split():
String match = "123woods";
String text = "I will come and meet you at the 123woods";

String[] sentence = text.split();
for(String word: sentence)
{
    if(word.equals(match))
        return true;
}
return false;

这是一种更简单、不那么优雅的方法,可以在不使用令牌等技术的情况下完成相同的事情。


虽然更易于理解和编写,但它并不是我所问问题的答案。我有两个、三个或者可能是无限数量的“匹配”关键字,我需要获取在“文本”中被找到的关键字。当然,你可以循环我的“匹配”关键字来匹配每一个分割后的“单词”,但我发现这远不如已经被接受的解决方案优雅。 - Nikola Yovchev

0
如果您想在字符串中识别一个完整的单词并更改该单词的内容,可以这样做。您最终的字符串保持不变,除非您处理的单词。在这种情况下,“not”在最终字符串中保持“'not'”。
    StringBuilder sb = new StringBuilder();
    String[] splited = value.split("\\s+");
    if(ArrayUtils.isNotEmpty(splited)) {
        for(String valor : splited) {
            sb.append(" ");
            if("not".equals(valor.toLowerCase())) {
                sb.append("'").append(valor).append("'");
            } else {
                sb.append(valor);
            }               
        }
    }
    return sb.toString();

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