LINUX.ORG.RU

Секретные алгоритмы пунто свитчера

 


0

1

Делаю чат бота. Люди ему иногда пишут сообщения забыв переключить раскладку. И получают ответ от бота - что за абракадарба, ничего не понимаю.

Хотелось бы улучшить этот момент, добавить авто определение неправильной раскладки.
Не хотелось бы изобретать свой велосипед, может кто знает простой надежный способ это делать.

Пунто свитчер уже с первых букв мог определить что раскладка перепутана, возможно у него был словарь обрезанный по нескольким первым буквам.

★★

Как я понимаю, это делается с помощью словаря частотности сочетаний букв в языке.

Вероятно, твой чат бот будет получать сообщение целиком, так что тебе подойдёт более простой алгоритм. Можно конвертировать текст в обе раскладки, разбивать на слова, а затем проверять в обоих версиях какой процент слов входит в словарь этого языка (словарь легко получить выкачав дамп какой-нибудь wiktionary и вырезав из него чисто заголовки статей). Если в альтернативной раскладке словарных попаданий больше, чем в исходной, то надо перевести раскладку. В целом тебе играет на руку то, что при неверной раскладке получается совсем бессмыслица, так что на одном из языков будет количество попаданий в словарь около нуля.

KivApple ★★★★★
()
Последнее исправление: KivApple (всего исправлений: 1)

Не делай этого. Просто добавь пункт меню «инвертировать раскладку». Я пользовался пунто свитчером. Он работает хорошо, но далеко не идеально. Если в чате бот будет периодически ломать раскладку из-за ошибочного срабатывания, то это будет сильно раздражать.

u5er ★★
()
Ответ на: комментарий от theurs

Статистические характеристики считать тоже сомнительно. И вообще не хочется велосипед собирать.

#!/usr/bin/env python3

from langdetect import detect


def detect_bad_layout(text: str) -> bool:
    r = detect(text)
    print(r)


l = ['ghbdtn',
     'привет',
     'нарисуй коня',
     'yfhbceq rjyz']

for x in l:
    detect_bad_layout(x)


da
mk
uk
cy

theurs ★★
() автор топика
Последнее исправление: theurs (всего исправлений: 2)

Пунто свитчер уже с первых букв мог определить что раскладка перепутана

Мог. Причём и когда надо мог, и когда не надо тоже мог. Всегда мог.

CrX ★★★★★
()
Ответ на: комментарий от theurs

В langdetect есть функция, которая возвращает таблицу с весами-вероятностями языков. Есть другие либы, например: fastlangdetect, langid.

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

Чат бот скорее всего понимает 1-2 языка, несложно самому сделать таблицы конвертации, тупо пройдясь по клавиатуре в разных раскладках. Сконвертировать и сравнить (только) веса нужных языков.

anonymous
()
Ответ на: комментарий от anonymous

Пусть бот спрашивает «вы хотели написать фигнянейм? да/нет?»

бот - гигантская нейросеть с 1.5трлн параметрами, напрягать ее тупыми дополнительными запросами не стоит

theurs ★★
() автор топика

Пунто свитчер уже с первых букв мог определить что раскладка перепутана

Щито? Он же просто выделенное слово по хоткею переделывал.

ya-betmen ★★★★★
()

может кто знает простой надежный способ это делать.

Простого и надёжного способа нет. Там идёт проверка по словарю 5-10к слов каждого слова по спец алгоритму, если близких совпадений нет, тоже делается и для другого языка. Если там какие-то совпадение есть, то пользователю предлагается этот вариант. Если нет, чекается другой язык. То есть получается минимум квадратичная асимптотика.

Ygor ★★★★★
()
Ответ на: комментарий от Ygor

Сделал на одном словаре. Взял словарь всех русский слов во всех формах - 1.5млн слов, и просто считаю их количество.
Вроде работает.
Но надо придумать кучу вариантов для проверки, что может не сработать.


#!/usr/bin/env python3


import binascii
import pickle
import re
import threading


LOCK = threading.Lock()


RU_WORDS = ''


def count_russian_words(text: str) -> int:
    """Считаем количество русских слов в тексте"""
    global RU_WORDS
    with LOCK:
        if isinstance(RU_WORDS, str):
            with open('russian_words_crc.dat', 'rb') as f:
                RU_WORDS = pickle.load(f)
            # RU_WORDS = [binascii.crc32(x.encode('cp1251', errors='replace')) for x in RU_WORDS]
            # with open('russian_words_crc.dat', 'wb') as f:
            #     pickle.dump(RU_WORDS, f)

    russian_words = []
    # Заменяем все символы, которых нет в алфавитах, на пробелы
    text = re.sub(r"[^а-яА-ЯіІїЇєЄёЁ]+", " ", text)
    for word in text.split():
        # Проверяем, является ли слово русским
        if binascii.crc32(word.encode('cp1251', errors='replace')) in RU_WORDS:
            russian_words.append(word)
    return len(russian_words)


def count_english_chars(text: str) -> int:
    """Считаем количество английских символов в тексте"""
    count = 0
    for char in text:
        if 'a' <= char <= 'z' or 'A' <= char <= 'Z':
            count += 1
    return count


def is_mostly_english(text: str) -> bool:
    """Проверяет, что количество английских букв в тексте больше 60%"""
    english_chars = count_english_chars(text)
    total_chars = len(text)
    if total_chars == 0:
        return False  # Avoid division by zero
    return (english_chars / total_chars) > 0.6


def convert_eng_to_rus(text: str) -> str:
    """Заменяет в тексте английские буквы на русские"""
    layout_mapping = {
        'q': 'й', 'w': 'ц', 'e': 'у', 'r': 'к',
        't': 'е', 'y': 'н', 'u': 'г', 'i': 'ш',
        'o': 'щ', 'p': 'з', '[': 'х', ']': 'ъ',
        'a': 'ф', 's': 'ы', 'd': 'в', 'f': 'а',
        'g': 'п', 'h': 'р', 'j': 'о', 'k': 'л',
        'l': 'д', ';': 'ж', '\'': 'э', 'z': 'я',
        'x': 'ч', 'c': 'с', 'v': 'м', 'b': 'и',
        'n': 'т', 'm': 'ь', ',': 'б', '.': 'ю',
        '/': '.', '`':'ё', '~': 'Ё',
        'Q': 'Й', 'W': 'Ц', 'E': 'У', 'R': 'К',
        'T': 'Е', 'Y': 'Н', 'U': 'Г', 'I': 'Ш',
        'O': 'Щ', 'P': 'З', '{': 'Х', '}': 'Ъ',
        'A': 'Ф', 'S': 'Ы', 'D': 'В', 'F': 'А',
        'G': 'П', 'H': 'Р', 'J': 'О', 'K': 'Л',
        'L': 'Д', ':': 'Ж', '"': 'Э', 'Z': 'Я',
        'X': 'Ч', 'C': 'С', 'V': 'М', 'B': 'И',
        'N': 'Т', 'M': 'Ь', '<': 'Б', '>': 'Ю',
        '?': ',',
    }
    result = ""
    for char in text:
        result += layout_mapping.get(char, char)
    return result


def count_all_words(text):
    """Подсчитывает количество слов в тексте, включая русские и английские."""
    words = re.findall(r"[a-zA-Zа-яА-ЯёЁ]+", text)
    return len(words)


def correct_layout(text: str) -> str:
    try:
        if is_mostly_english(text):
            converted = convert_eng_to_rus(text)
            all_words = count_all_words(text)
            rus_words = count_russian_words(converted)
            if rus_words > all_words/2:
                return converted
    except Exception as error:
        print(error)
    return text


l = [
    'ghbdtn',
    'привет',
    'нарисуй коня',
    'yfhbceq rjyz',
    'ye b xt',
    'Съешь ещё этих мягких французских булок, да выпей чаю',  # Russian
    'The quick brown fox jumps over the lazy dog.',  # English
    'This is a mixed text with both English and Russian words: привет!',
    'Ghbdtn, rfr ltkf ghjcnj',  # Russian in English layout
    'Привет, это тест на русском языке.',  # Russian
    'This is another test, but in English.', #!
    'Just some random English words: keyboard, mouse, monitor.',
    'Ещё немного русских слов: солнце, небо, облака.',
    'Ytnrf yt gjcktlyz vs ghbdt',  # Mixed Russian and English in English layout
    '~, ndj. yfktdj',
    'Lf ,kznm ye xj jgznm',
    ]

for x in l:
    print(correct_layout(x))

theurs ★★
() автор топика
Ответ на: комментарий от theurs

Сорян, я питон не знаю, 1,5 млн это очень много, общеупотребимых край 5-10к. Я бы сравнивал слова на основе КМП алгоритма, или дистанции Левенштейна, или ещё чего либо что работает быстрее. Но в целом это долго, как в топике правильно заметили, лучше отдать это на откуп пользователю, чтобы он нажимал изменить раскладку.

Ygor ★★★★★
()
Ответ на: комментарий от theurs

Этот вариант не срабатывает если в словах есть некритичные ошибки - привет-превет

Естественно, поэтому нужны алгоритмы например дистанция Левенштейна, вычисляешь дистанцию между введённым словом и словами из словаря и предлагаешь набор из слов с минимальной дистанцией. Это в целом не сложно, но надо ли, тут думай сам.

Ygor ★★★★★
()
Последнее исправление: Ygor (всего исправлений: 1)

Хотелось бы улучшить этот момент, добавить авто определение неправильной раскладки. Не хотелось бы изобретать свой велосипед, может кто знает простой надежный способ это делать.

Тебе же не нужно с первых нескольких букв, достаточно когда пришло сообщение. Если боту хочется сказать «что за абракадарба, ничего не понимаю.», то попробуй отобразить (map) буквы по раскладке и понять ещё раз. Если опять не получилось, тогда говори «что за абракадарба, ничего не понимаю.»

Gentooshnik ★★★★★
()
Последнее исправление: Gentooshnik (всего исправлений: 1)
Ответ на: комментарий от theNamelessOne

Лол, прям вот тебе готовая реализация нечёткого поиска слова в prefix tree (на протухшем питоне). С ней простой алгоритм может быть такой:

  1. Загоняем в trie словари соответствующих языков.
  2. Полученную строку «переводим» на другой язык сменой раскладки.
  3. Каждую строку разбиваешь на слова (в соответствии с правилами языка), выбрасываешь короткие слова, ищешь для каждого слова совпадения.

Смотришь, для какого языка совпадений больше.

theNamelessOne ★★★★★
()
Ответ на: комментарий от theNamelessOne

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

Так что придется бежать по всему списку и делать нечеткое сравнение пока не найдешь достаточно большое совпадение. Это очень долго, и вообще может создать еще большую ошибку.

theurs ★★
() автор топика
Ответ на: комментарий от anonymous

цитата из википедии: Если собрать все чертыхания компьютерщиков, когда текст печатается не в той раскладке, то мы получили бы энергию, равную одной атомной бомбе, а может, и двум.

гигачад от сбера
https://kunsun-imgs.dns.army/i/4690c77a-df1b-4376-985b-7b1c21ac0d45.jpg


а так это должно было отработать
https://kunsun-imgs.dns.army/i/0bc936c0-44cf-447b-ab67-3b26fc825d5e.jpg

theurs ★★
() автор топика
Ответ на: комментарий от theurs

Так что придется бежать по всему списку и делать нечеткое сравнение пока не найдешь достаточно большое совпадение. Это очень долго, и вообще может создать еще большую ошибку.

Trie — это дерево, поиск в нем гораздо быстрый.

theNamelessOne ★★★★★
()
Последнее исправление: theNamelessOne (всего исправлений: 1)
Ответ на: комментарий от theurs

Сделал еще 1 вариант на триграмах. Текст разбивается на тройки символов и по заранее составленной таблице вероятностей считается сумма. Получился такой словарь слогов. Вроде нормально работает. https://github.com/theurs/tb1/blob/master/my_correct_layout_ru_v2.py

А на каких словах пунто свитчер фейлился?

theurs ★★
() автор топика

Поставил punto switcher, посмотреть. Яша как всегда, только и делает, что вызывает чувство брезгливости. Шпионские зонды даже в такую крохотулю засунули. Вот зачем ему долбится на свои сервера, если я отключил автообновление? Могли бы открыть исходники, на репутацию и плюс в карму.

anonymous
()

И получают ответ от бота - что за абракадарба, ничего не понимаю.

Хотелось бы улучшить этот момент

А по-моему, как раз сейчас логичное поведение. Пусть кожаные мешки по-русски пишут грамотно и за свою раскладку отвечают сами. А то ведь доулучшаешься до того, что человек напишет неоднозначно трактуемую абракадабру, бот её трактует неправильно, а виноват внезапно окажешься ты.

Пусть они себе пунто свитчер или аналоги ставят, если за раскладкой уследить не могут. И сами отвечают за то, что на выходе свитчера получается.

hobbit ★★★★★
()
Ответ на: комментарий от hobbit

Пусть кожаные мешки … пишут

Окей, гугл, ой, Алиса, или Маруся, забыл, как звать, величать, салют, есть кто дома, напиши этому боту абракадабру.

anonymous
()