Проверьте, является ли строка словом или частью слова в Tkinter Text Widget

Я работаю над проверкой орфографии для текстового виджета tkinter. У меня это работает, так что пользователь может выбрать неправильное слово и заменить все экземпляры неправильного слова в текстовом виджете. Однако, если слово появляется в другом слове, оно также заменит его. Я не хочу этого

Например: скажем, у пользователя было предложение: Hello how ay you today И они пропустили слово " есть " как " ау ", они могли бы щелкнуть по нему правой кнопкой мыши, чтобы заменить все экземпляры или слово " ау " на " есть ".

Моя проблема в том, что строка " ай " появляется в " сегодня ". Это означает, что когда пользователь щелкает правой кнопкой мыши на " ay ", он превращает " сегодня " в " todare " - заменяя " ay " в " сегодня " на " являются "

Для замены слова я использую функцию поиска. Я думал о том, чтобы проверить, были ли символы по обе стороны от пропущенного слова пробелами, но я не знал, как это реализовать. Вот мой код ниже (обратите внимание - это значительно упрощено, и мой настоящий код длиной в тысячи строк. В настоящей программе кнопка представляет собой контекстное меню):

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 ' в ' hello '.

ЭТО: ll ll hello -> I'll I'll hello НЕ ll ll hello -> I'll I'll heI'llo

Спасибо за вашу помощь.

(Я использую Windows 10 с Python 3.7)

1 ответ

Решение

Решением вашей проблемы является использование регулярных выражений. Регулярные выражения позволяют вам искать больше, чем просто текст. Вы также можете искать шаблоны и другие метасимволы. Например, выражение может соответствовать только строке в начале строки или в начале слова.

В вашем случае вы хотите найти целые слова. В контексте текстового виджета search метод, можно найти целое слово, окружив искомую строку \m (начало слова) и \M (конец слова).

Например, чтобы искать "ll" только как целое слово, вы должны искать \mll\M, Поскольку обратная косая черта в Python является специальным символом, и нам необходимо передать обратную косую черту search метод, он должен быть защищен. Самый простой способ - использовать необработанную строку.

Итак, учитывая слово в переменной (например: word="ll"), мы можем сделать шаблон, который выглядит следующим образом:

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

Чтобы использовать этот шаблон в поиске, нам нужно установить regexp параметр search метод для 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()

Обзор регулярных выражений см. В документации по модулю re.

Регулярные выражения, используемые в текстовом виджете search Метод немного отличается от регулярных выражений Python. Например, Python использует \b означать начало или конец слова, тогда как search метод использует \m а также \M, Для подробного объяснения синтаксиса выражения, используемого search см. справочную страницу Tcl re_syntax

Другие вопросы по тегам