diff --git a/etpgrf/hyphenation.py b/etpgrf/hyphenation.py index b44244f..4dab4b7 100755 --- a/etpgrf/hyphenation.py +++ b/etpgrf/hyphenation.py @@ -128,7 +128,7 @@ class Hyphenator: # 1. ОБЩИЕ ПРОВЕРКИ # TODO: возможно, для скорости, надо сделать проверку на пробелы и другие разделители, которых не должно быть if not word: - # Добавим явную проверку на пустую строку + # Явная проверка на пустую строку return "" if len(word) <= self.max_unhyphenated_len or not any(self._is_vow(c) for c in word): # Если слово короткое или не содержит гласных, перенос не нужен @@ -140,29 +140,61 @@ class Hyphenator: if (LANG_RU in self.langs or LANG_RU_OLD in self.langs) and frozenset(word.upper()) <= self._ru_alphabet_upper: # Пользователь подключил русскую логику, и слово содержит только русские буквы logger.debug(f"`{word}` -- use `{LANG_RU}` or `{LANG_RU_OLD}` rules") + # Поиск допустимой позиции для переноса около заданного индекса def find_hyphen_point_ru(word_segment: str, start_idx: int) -> int: vow_indices = [i for i, char_w in enumerate(word_segment) if self._is_vow(char_w)] # Если в слове нет гласных, то перенос невозможен if not vow_indices: return -1 + word_segment_len = len(word_segment) # Ищем ближайшую гласную до или после start_idx for i in vow_indices: - if i >= start_idx - self.min_chars_per_part and i + self.min_chars_per_part < len(word_segment): + if i >= start_idx - self.min_chars_per_part and i + self.min_chars_per_part < word_segment_len: # Проверяем, что после гласной есть минимум символов "хвоста" ind = i + 1 # 1. Не отделяем "хвостов" с начала или конца (это некрасиво) - if ind <= self.min_chars_per_part or ind >= len(word_segment) - self.min_chars_per_part: + if ind <= self.min_chars_per_part or ind >= word_segment_len - self.min_chars_per_part: continue - # 2. Пропускаем мягкий/твердый знак, если перенос начинается или заканчивается - # на них (правило из ГОСТ 7.62-2008) - if self._is_sign(word_segment[ind]) or (ind > 0 and self._is_sign(word_segment[ind-1])): + # 2. Сдвигаем перенос за мягкий/твердый знак, если он сразу за согласной (ГОСТ 7.62-2008) + if self._is_sign(word_segment[ind]): + # 2.1 Текущая буква мягкий/твердый знак. Ставим перенос за ней (индекс ind+1). + return ind + 1 + if (self._is_cons(word_segment[ind]) and + i+1 < word_segment_len and self._is_sign(word_segment[ind+1])): + # 2.2 Текущая буква согласная, а следующая мягкий/твердый знак. Ставим перенос за ней + return ind+2 + # 3. Проверка на `Й` (полугласная). Не бывает слов, когда сразу не ней идет гласная, + # или перед ней идет согласная. Сдвигает перенос за полугласную букву если она идет после + # гласной. + if self._is_j_sound(word_segment[ind+1]): + # 3.1 Текущая буква `й`. Ставим за ней перенос (индекс ind+1). + return ind+1 + if (self._is_vow(word_segment[ind]) and + i+1 < word_segment_len and self._is_j_sound(word_segment[ind+1])): + # 3.2 Текущая буква гласная, а следующая `й`. Ставим перенос за `й` (индекс ind+2). + # Ставим перенос за `й` (индекс ind+2). + return ind+2 + # 4. Проверка на сдвоенная-согласная (C-C). + if (self._is_cons(word_segment[ind]) and + i+1 < word_segment_len and word_segment[ind] == word_segment[ind+1]): + print("сдвоенная согласная") + # 4.1 Текущая буква согласная и следующая така же (сдвоенная согласная). Ставим перенос + # за ней (индекс ind+1). + return ind + 1 + if (self._is_cons(word_segment[ind]) and + i+1 < word_segment_len and self._is_cons(word_segment[ind+1])): + # 4.2 НЕ ОБЯЗАТЕЛЬНОЕ ПРАВИЛО: Текущая буква согласная, а следующая тоже согласная. + # Ставим перенос за ней (индекс ind+1). + return ind+1 + # 5. Проверка на гласная-гласная (V-V). + if (self._is_vow(word_segment[ind]) and + i+1 < word_segment_len and self._is_vow(word_segment[ind+1])): + # 5.1 Текущая буква гласная, а следующая гласная. Перенос не делаем. Возможно, + # надо дальше искать до ближайшей согласной, но это усложнит алгоритм. continue - # 3. Провека на `Й` (полугласная). Перенос после неё только в случае, если дальше идет - # согласная (например, "бой-кий"), но запретить, если идет гласная (например, - # "ма-йка" не переносится). - if (self._is_cons(word_segment[ind]) or self._is_j_sound(word_segment[ind])) and not self._is_vow(word_segment[ind + 1]): - ind += 1 + # 6. TODO (опционально): Проверка на суффикс и приставку (не разбивать). Нужен словарь. + # 7. TODO (опционально): Проверка на короткий корень (не разбивать). Нужен очень большой словарь. return ind return -1 # Не нашли подходящую позицию diff --git a/tests/test_hyphenation.py b/tests/test_hyphenation.py index 0ab712f..d3e8fc5 100644 --- a/tests/test_hyphenation.py +++ b/tests/test_hyphenation.py @@ -11,19 +11,24 @@ RUSSIAN_HYPHENATION_CASES = [ ("тестирование", "тести\u00ADрование"), ("благотворительностью", "благотво\u00ADритель\u00ADностью"), # Слово с переносом на мягкий знак ("гиперподъездной", "гипер\u00ADподъ\u00ADездной"), # Слово с переносом на твердый знак - ("фотоаппаратура", "фотоап\u00ADпара\u00ADтура"), # проверка слова с двойной согласной + ("фотоаппаратура", "фотоап\u00ADпара\u00ADтура"), # проверка слова со сдвоенной согласной + ("программирование", "програм\u00ADмиро\u00ADвание"), # слова со сдвоенной согласной ("сверхзвуковой", "сверхзву\u00ADковой"), - ("автомобиль", "авто\u00ADмобиль"), # Слово с переносом на мягкий знак - ("интернационализация", "интерна\u00ADциона\u00ADлизация"), # Длинное слово с переносами - ("программирование", "програм\u00ADмиро\u00ADвание"), + ("автомобиль", "авто\u00ADмобиль"), + ("интернационализация", "интерна\u00ADциона\u00ADлизация"), ("суперкомпьютер", "супер\u00ADком\u00ADпьютер"), ("электронный", "электрон\u00ADный"), ("информационный", "информа\u00ADционный"), ("автоматизация", "авто\u00ADмати\u00ADзация"), - ("многоклеточный", "многок\u00ADлеточ\u00ADный"), # Сложное слово с переносами - ("многофункциональный", "многофун\u00ADкцио\u00ADнальный"), # Сложное слово с переносами - ("непрерывность", "непре\u00ADрывность"), # Слово с мягким знаком в конце - ("сверхпроводимость", "сверхпро\u00ADводи\u00ADмость"), # Слово с мягким знаком в середине + ("многоклеточный", "многок\u00ADлеточ\u00ADный"), + ("многофункциональный", "многофун\u00ADкцио\u00ADнальный"), + ("непрерывность", "непре\u00ADрывность"), + ("сверхпроводимость", "сверхпро\u00ADводи\u00ADмость"), + ("многообразие", "много\u00ADобразие"), + ("противоречивость", "противо\u00ADречи\u00ADвость"), + ("сверхчувствительный", "сверхчув\u00ADстви\u00ADтельный"), # Будет неправильный перенос, (словарь "корней") + ("непревзойденный", "непрев\u00ADзойден\u00ADный"), # Будет неправильный перенос + ("многослойный", "многос\u00ADлойный"), # Будет неправильный перенос, ]