首页 > 解决方案 > 检查字符串是否是 Tkinter Text Widget 中的单词或单词的一部分

问题描述

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

例如:假设用户有句子: Hello how ay you today 并且他们错过了将单词' are '拼写为' ay ',他们可以右键单击它以替换所有实例或将单词' ay '替换为' are '。

我的问题是,字符串“ ay ”出现在“今天”中。这意味着当用户右键单击“ ay ”时,它会将“ today ”变成“ todare ”——将“ 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 ' 的单词,而不是替换'你好'中的' ll '。

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

谢谢你的帮助。

(我正在使用带有 Python 3.7 的 Windows 10)

标签: pythontkintertkinter.text

解决方案


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

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

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

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

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

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

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

一旦我们完成了所有这些,我们就可以在文本小部件中搜索字符串“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()

截屏

有关正则表达式的概述,请参阅re 模块的文档

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


推荐阅读