有没有办法在不和谐消息中找到单词的确切字符串?

Is there a way to find the EXACT string of a word in a discord message?

目前我正在开发一个过滤消息的 discord 机器人。我的问题发生在尝试过滤包含在其他单词中的单词时,从而触发重复消息。

这是我的 filter.txt:

sad
sadness
sadnesses

因为“sad”也可以在“sadness”中找到,所以每当写“sadness”时,我都会得到“sad”的误报。
是否可以只检测消息中的确切字符串?喜欢:I want to be happy, because sadness is bad → '检测悲伤'

希望你明白我的意思。

代码:

public void onGuildMessageReceived(GuildMessageReceivedEvent e) {
    File file = new File("src/filter.txt");
    try {
        BufferedReader br = new BufferedReader(new FileReader(file));
        String line;
        while ((line = br.readLine()) != null) {
            if(!line.startsWith("#")) {
                if(e.getMessage().getContentRaw().contains(line)) {
                    User user = e.getJDA().getUserById(e.getAuthor().getIdLong());
                    e.getMessage().delete().queue();
                    user.openPrivateChannel().queue(privateChannel -> {
                        privateChannel.sendMessage("Bitte achte auf deine Sprache!").queue();
                    });
                }                   
            }
        }
    } catch (IOException e1) {}
}

正如 Cardinal - Reinstate MonicaHades 已经说过的,你应该看看正则表达式。

'Regex' 代表 'Regular expression' 并描述字符串的搜索模式。

使用正则表达式可以做很多事情,所以如果您想了解更多信息,请查看 tutorial
(这是我在谷歌搜索时发现的第一个,当然你可以使用你喜欢的任何教程。)

对于您的用例,我建议如下:

首先,不要使用 String.contains(),因为它只适用于字符串,不适用于正则表达式。
使用 String.matches() 代替以下正则表达式:

"(?is).*\bSTRING\b.*"

因为完成了一些转义,这就是没有它的正则表达式的样子:

(?is).*\bSTRING\b.*

我会解释它是如何工作的。

\b

\b 匹配单词边界。单词字符为 a - zA - Z0 - 9_。这些字符的任意组合都被视为一个词。
这样做的好处是,您可以在以下情况下匹配单词 sad

  • “我很难过。” → 句尾的.不影响检测
  • "sad is my thing" → 这个词即使是第一个也是匹配的。 (这也受到.*的影响。)

当使用sadness时,它不会匹配sad,因为后面的词继续:

  • “我感到悲伤!” → 因为“sad”后面没有结尾,所以不匹配。匹配“悲伤”会起作用。

.*

. 匹配除某些换行符之外的任何字符。 ((?s) 在这里帮助我。)
* 基本上是说,它前面的部分出现了零次或多次。
通过在字符串前后使用 .*,正则表达式可以在字符串周围使用 任何 字符或字符组合(包括无字符)。
这很重要,因为通过这种方式,单词可以放在每个可以想象的句子中,并且无论如何都会匹配。

(?是)

?i?s 启用某些模式。
?i 使正则表达式不区分大小写。这意味着,无论是 sadnessSADNESS 还是 sAdNeSs;所有三个都会匹配。
?s 启用 'single line mode',这意味着 . 也匹配所有换行符。
?i?s 可以组合成 (?is) 然后放在正则表达式的前面。

而不是 STRING 你只需要像这样插入你的话:

"(?is).*\b" + line + "\b.*"

你的代码最后看起来像这样:

public void onGuildMessageReceived(GuildMessageReceivedEvent e) {
    File file = new File("src/filter.txt");
    try {
        BufferedReader br = new BufferedReader(new FileReader(file));
        String line;
        while ((line = br.readLine()) != null) {
            if(!line.startsWith("#")) {
                if(e.getMessage().getContentRaw().matches("(?is).*\b" + line + "\b.*")) {
                    User user = e.getJDA().getUserById(e.getAuthor().getIdLong());
                    e.getMessage().delete().queue();
                    user.openPrivateChannel().queue(privateChannel -> {
                        privateChannel.sendMessage("Bitte achte auf deine Sprache!").queue();
                    });
                }  
            }
        }
    } catch (IOException e1) {}
}

如果您希望它只为每条消息生成一条消息(因此在第一次匹配后停止),您可以在匹配一个词并向用户发送消息后插入一个 return;

您也可以尝试使用字符串搜索算法,例如 Aho-Corasick,但这需要实施适当的签名 table。像这样的算法在更大的单词列表中会好得多。

请注意,此类算法很容易被规避。简单地添加空格或使用 1337 字符替换将胜过天真的单词过滤器。