有没有办法在不和谐消息中找到单词的确切字符串?
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 Monica 和 Hades 已经说过的,你应该看看正则表达式。
'Regex' 代表 'Regular expression' 并描述字符串的搜索模式。
使用正则表达式可以做很多事情,所以如果您想了解更多信息,请查看 tutorial。
(这是我在谷歌搜索时发现的第一个,当然你可以使用你喜欢的任何教程。)
对于您的用例,我建议如下:
首先,不要使用 String.contains()
,因为它只适用于字符串,不适用于正则表达式。
使用 String.matches()
代替以下正则表达式:
"(?is).*\bSTRING\b.*"
因为完成了一些转义,这就是没有它的正则表达式的样子:
(?is).*\bSTRING\b.*
我会解释它是如何工作的。
\b
\b
匹配单词边界。单词字符为 a
- z
、A
- Z
、0
- 9
和 _
。这些字符的任意组合都被视为一个词。
这样做的好处是,您可以在以下情况下匹配单词 sad:
- “我很难过。” → 句尾的
.
不影响检测
- "sad is my thing" → 这个词即使是第一个也是匹配的。 (这也受到
.*
的影响。)
当使用sadness时,它不会匹配sad,因为后面的词继续:
- “我感到悲伤!” → 因为“sad”后面没有结尾,所以不匹配。匹配“悲伤”会起作用。
.*
.
匹配除某些换行符之外的任何字符。 ((?s)
在这里帮助我。)
*
基本上是说,它前面的部分出现了零次或多次。
通过在字符串前后使用 .*
,正则表达式可以在字符串周围使用 任何 字符或字符组合(包括无字符)。
这很重要,因为通过这种方式,单词可以放在每个可以想象的句子中,并且无论如何都会匹配。
(?是)
?i
和 ?s
启用某些模式。
?i
使正则表达式不区分大小写。这意味着,无论是 sadness、SADNESS 还是 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 字符替换将胜过天真的单词过滤器。
目前我正在开发一个过滤消息的 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 Monica 和 Hades 已经说过的,你应该看看正则表达式。
'Regex' 代表 'Regular expression' 并描述字符串的搜索模式。
使用正则表达式可以做很多事情,所以如果您想了解更多信息,请查看 tutorial。
(这是我在谷歌搜索时发现的第一个,当然你可以使用你喜欢的任何教程。)
对于您的用例,我建议如下:
首先,不要使用 String.contains()
,因为它只适用于字符串,不适用于正则表达式。
使用 String.matches()
代替以下正则表达式:
"(?is).*\bSTRING\b.*"
因为完成了一些转义,这就是没有它的正则表达式的样子:
(?is).*\bSTRING\b.*
我会解释它是如何工作的。
\b
\b
匹配单词边界。单词字符为 a
- z
、A
- Z
、0
- 9
和 _
。这些字符的任意组合都被视为一个词。
这样做的好处是,您可以在以下情况下匹配单词 sad:
- “我很难过。” → 句尾的
.
不影响检测 - "sad is my thing" → 这个词即使是第一个也是匹配的。 (这也受到
.*
的影响。)
当使用sadness时,它不会匹配sad,因为后面的词继续:
- “我感到悲伤!” → 因为“sad”后面没有结尾,所以不匹配。匹配“悲伤”会起作用。
.*
.
匹配除某些换行符之外的任何字符。 ((?s)
在这里帮助我。)
*
基本上是说,它前面的部分出现了零次或多次。
通过在字符串前后使用 .*
,正则表达式可以在字符串周围使用 任何 字符或字符组合(包括无字符)。
这很重要,因为通过这种方式,单词可以放在每个可以想象的句子中,并且无论如何都会匹配。
(?是)
?i
和 ?s
启用某些模式。
?i
使正则表达式不区分大小写。这意味着,无论是 sadness、SADNESS 还是 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 字符替换将胜过天真的单词过滤器。