检查字符串是 Tkinter Text Widget 中的单词还是单词的一部分

Check if a string is a word or part of a word in Tkinter Text Widget

我正在为 tkinter 文本小部件开发拼写检查器。我让它工作,以便用户可以 select 一个不正确的词并替换文本小部件中不正确词的所有实例。 但是,如果该词 出现在 另一个词中,它也会替换它。我不要这个。

例如: 假设用户有一句话: Hello how ay you today 他们将单词“are”拼错为“ay”,他们可以右键单击它以替换所有实例或单词'ay' 和 'are'。

我的问题是,字符串“ay”出现在“today”中。这意味着当用户右键单击“ay”时,它将“today”变成“todare[=54=” ]' - 将'today'中的'ay'替换为'are'

要替换我正在使用搜索功能的单词。我想检查一下拼错的单词两边的字符是否是空格,但我不知道如何实现它。 下面是我的代码(注意——这已经大大简化了,我的实际代码有数千行。在实际程序中,按钮是上下文菜单):

from spellchecker import SpellChecker

root = Tk()
notepad = Text(root)
notepad.pack()

spell_dict = SpellChecker()


def check_spelling(event):
    global spell_dict

    misspelt_words_list = [] 
    paragraph_list = notepad.get('1.0', END).strip('\n').split()

    notepad.tag_config('misspelt_word_tag', foreground='red', underline=1)

        for word in paragraph_list:

            if (word not in spell_dict) and (word not in  misspelt_words_list):
                    misspelt_words_list.append(word)

            elif (word in misspelt_words_list) and (word in spell_dict):
                misspelt_words_list.remove(word)

    notepad.tag_remove('misspelt_word_tag', 1.0, END)

    for misspelt_word in misspelt_words_list:
        misspelt_word_offset = '+%dc' % len(misspelt_word) 

        pos_start = notepad.search(misspelt_word, '1.0', END)

        while pos_start:

            pos_end = pos_start + misspelt_word_offset
                notepad.tag_add("misspelt_word_tag",pos_start,pos_end)

            pos_start = notepad.search(misspelt_word,pos_end,END)


button = Button(root, text = "This is a test", command = check_spelling)
button.pack()

root.mainloop() 

就像我之前说的,如果用户写 ll ll hello,其中 'll' 是拼错的(假设程序会将其更正为 I'll) ,当用户按下按钮时,它应该替换所有写成'll'的words,但不会替换'll' 在'你好'。

这个: ll ll hello -> I'll I'll hello, 不是: ll ll hello -> I'll I'll heI'llo

感谢您的帮助。

(我正在使用 Windows 10 和 Python 3.7)

您的问题的解决方案是使用 regular expressions。正则表达式让您搜索的不仅仅是文本。您还可以搜索模式和其他元字符。例如,表达式只能匹配行首或词首的字符串。

在你的例子中,你想要找到完整的单词。在文本小部件 search 方法的上下文中,可以通过用 \m(单词开头)和 \M(单词结尾)包围您正在搜索的字符串来搜索整个单词字)。

例如,要仅搜索整个单词 "ll",您应该搜索 \mll\M。因为反斜杠在python中是一个特殊字符,我们需要将反斜杠传递给search方法,所以需要对其进行保护。最简单的方法是使用原始字符串。

因此,给定变量中的一个词(例如:word="ll"),我们可以制作如下所示的模式:

pattern = r'\m{}\M'.format(word)

要在搜索中使用该模式,我们需要将 search 方法的 regexp 参数设置为 True。还有一些其他事情需要完成。我们想让 search 方法告诉我们有多少字符与模式匹配。在搜索 "ll" 的情况下,我们知道它总是两个字符,但一个好的通用解决方案是让搜索机制告诉我们。我们可以通过将 IntVar 传递给 search 方法来做到这一点。

我们需要做的另一件事是确保搜索在小部件的末尾停止,否则,它将绕回开头并永远继续搜索。

一旦我们准备好所有这些,我们就可以在文本小部件中搜索字符串 "ll",仅作为整个单词,如下所示:

countvar = IntVar()
pos = "1.0"
pattern = r'\mll\M'

pos = notepad.search(pattern, pos, "end", count=countvar, regexp=True)
pos_end = notepad.index("{} + {} chars".format(pos, countvar.get()))

因此,pos 标志着比赛的开始,pos_end 标志着比赛的结束。如果 pos 是空字符串,那么我们知道 tkinter 没有找到匹配项(在这种情况下我们可以跳过计算 pos_end)。

总而言之,我们可以创建一个通用函数来查找并突出显示列表中的所有单词,如下所示:

def highlight_words(widget, tag, word_list):
    """Find all whole words in word_list and apply the given tag"""
    widget.tag_remove(tag, "1.0", END)

    countvar = IntVar()
    for word in word_list:
        pos = "1.0"
        pattern = r"\m{}\M".format(word)
        while widget.compare(pos, "<", "end"):
            pos = widget.search(pattern, pos, "end", count=countvar, regexp=True)
            if pos:
                pos_end = widget.index("{} + {} chars".format(pos, countvar.get()))
                widget.tag_add(tag,pos,pos_end)
                pos = pos_end
            else:
                break

我们可以这样使用这个函数:

root = Tk()
notepad = Text(root)
notepad.pack()
notepad.tag_configure("misspelt_word_tag", background="pink")

notepad.insert("end", "ll ll hello")
misspelt_word_list = ['ll']
highlight_words(notepad, "misspelt_word_tag", misspelt_word_list)

root.mainloop()

有关正则表达式的概述,请参阅 documentation for the re module

文本小部件 search 方法中使用的正则表达式与 python 正则表达式略有不同。例如,python 使用 \b 表示单词的开头或结尾,而 search 方法使用 \m\M。有关 search 方法使用的表达式语法的详细说明,请参阅 Tcl 的 re_syntax man page