Проверьте, является ли строка словом или частью слова в 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