Как токенизировать пиньинь, предпочтительно используя вложенные, пересекающиеся группы регулярных выражений? - PullRequest
0 голосов
/ 09 февраля 2019

Я пытаюсь токенизировать китайскую нотацию пиньинь (без тонов).Рассмотрим следующий код:

finals = ['a',
        'o',
        'e',
        'ai',
        'ei',
        'ao',
        'ou',                                                                                                                                                                       
        'an',                                                                                                                                                                       
        'ang',
        'en',
        'eng',
        'er',
        'u',
        'ua',
        'uo',
        'uai',
        'ui',
        'uan',
        'uang',
        'un',
        'ueng',
        'ong',
        'i',
        'i',
        'ia',
        'ie',
        'iao',
        'iu',
        'ian',
        'iang',
        'in',
        'ing',
        'ü',
        'üe',
        'üan',
        'ün',
        'iong']
initials = ['p',
          'm',
          'f',
          'd',
          't',
          'n',
          'l',
          'g',
          'k',
          'h',
          'j',
          'q',
          'x',
          'z',
          'h',
          'c',
          'h',
          's',
          'h',
          'r',
          'z',
          'c',
          's']
others = ['a',
        'o',
        'e',
        'ai',
        'ei',
        'ao',
        'ou',
        'an',
        'ang',
        'en',
        'eng',
        'er',
        'wu',
        'wa',
        'wo',
        'wai',
        'wei',
        'wan',
        'wang',
        'wen',
        'weng',
        'yi',
        'ya',
        'ye',
        'yao',
        'you',
        'yan',
        'yang',
        'yin',
        'ying',
        'yu',
        'yue',
        'yuan',
        'yun',
        'yong']

r = '^((%s)(%s)|%s)+$' % ('|'.join(initials), '|'.join(finals), '|'.join(others))
import re
m = re.match(r, 'yinwei')
print(m.groups())

Я надеялся получить ['yin','wei'] (две последовательные внешние группы), но по какой-то причине получил только «wei».Почему этот код не работает и как это исправить?Я также попробовал следующее, но оно случайно либо дает мне ['yin', 'wei'] или ['yi', 'wei]:

import regex
r = '|'.join({i + f for i in initials for f in finals}.union(set(others)))
print(regex.findall(r, 'yinwei'))

РЕДАКТИРОВАТЬ: я собирался принять это как дубликат 4963691 из-заответ эхумуро, но он не работает с bangongshi в качестве ввода - вместо ['ban','gong','shi'] мы получаем ['bang', 'o', 'shi'].В связи с этим я хотел бы, чтобы этот вопрос рассматривался отдельно от этого .

1 Ответ

0 голосов
/ 09 февраля 2019

Модуль re не накапливает группы при использовании с такими операторами, как +.В вашем примере он сначала будет соответствовать 'yin', а затем 'wei' - но он сохранит только последний набор соответствующих групп (поэтому m.groups() вернет только ['wei', None, None]).Тем не менее, ваше регулярное выражение все равно правильно получит полное совпадение - поэтому m.group() вернет 'yinwei'.

Похоже, что элементы в ваших списках не дают никаких перекрывающихся комбинаций.То есть: нет initials[n] + finals[n], который дублируется в others.Тем не менее, есть перекрывающиеся элементы внутри каждого списка (например, yi|yin|ying в others), но это можно преодолеть, отсортировав списки по убыванию длины.

Это означает, что вы можете довольнолегко разбить пиньиньское слово на его элементы следующим образом:

import re

initials.sort(key=len, reverse=True)
finals.sort(key=len, reverse=True)
others.sort(key=len, reverse=True)

r = '(?:%s)(?:%s)|(?:%s)' % ('|'.join(initials), '|'.join(finals), '|'.join(others))
print(re.findall(r, 'yinwei'))

Вывод:

['yin', 'wei']

ОБНОВЛЕНИЕ :

После просмотра достоверный источник , похоже, ваш метод разбора пиньинь слишком упрощен.Таблица комбинаций показывает, что не все возможности являются действительными.Это также показывает, что некоторые комбинации неоднозначны (с чисто синтаксической точки зрения).Например, liang может быть проанализировано как [l + iang] или [l + i], [ang].Кроме того, не все продолжения действительны, поэтому потребуются некоторые предварительные утверждения.Это говорит о том, что потребуется гораздо более сложный подход, чем просто последовательное сопоставление слева направо.После некоторых поисков я обнаружил предыдущий вопрос, который, по-видимому, охватывает те же проблемы:

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

...