CS50作业:敏感词过滤时子串误匹配的问题排查
嘿,我来帮你拆解下为什么第二个版本会出现子串误替换的问题,以及怎么修复它~
核心原因:两种检查方式的本质区别
1. 正常运行的第一个版本逻辑
你第一个版本里的(word_list[i].lower() + '\n') in fin是在文件对象中查找匹配项。当你对文件对象使用in操作时,Python会逐行遍历文件,检查是否有某一行完全等于你传入的字符串(这里是单词小写+换行符)。因为你的敏感词字典应该是每行存一个敏感词,所以这个写法正好匹配完整的敏感词行,不会误判子串。
每次循环后调用fin.seek(0)是把文件指针移回开头,确保下一次检查能重新从文件第一行开始遍历,这个逻辑是没问题的。
2. 出错的第二个版本问题
第二个版本用了word_list[i].lower() in fin.read(),这里的fin.read()会一次性把整个文件的内容读取成一个完整的字符串(包含所有换行符、空格等)。比如你的字典里有一行heck\n,那这个大字符串里就会包含heck、hec、he、h这些子串。当你检查he是否在这个字符串里时,结果自然是True,所以就会误把包含这些子串的单词也替换掉。
哪怕你每次循环后调用fin.seek(0),也只是让下一次fin.read()重新读一遍文件内容,但本质还是在大字符串里找子串,无法避免误匹配。
更优的修复方案
其实还有更高效且精确的写法:把敏感词读取到一个集合里,集合的查找是O(1)的,比逐行遍历文件快得多,还能精确匹配完整单词。
修改后的代码可以这样写:
from cs50 import get_string from sys import argv def main(): if len(argv) != 2: print("Usage: python bleep.py dictionary") exit(1) # 将敏感词读取到集合中,统一转小写并去掉换行符 with open(argv[1]) as fin: bad_words = {line.strip().lower() for line in fin} print("What message would you like to censor?") message = get_string() word_list = message.split() # 遍历检查每个单词 for i in range(len(word_list)): if word_list[i].lower() in bad_words: word_list[i] = '*' * len(word_list[i]) print(' '.join(word_list)) if __name__ == "__main__": main()
这里用with语句自动管理文件关闭,不用手动调用fin.close();集合bad_words存储所有小写的敏感词,检查时直接判断单词小写形式是否在集合里,既精确又高效。
内容的提问来源于stack exchange,提问作者shayan

