Регулярное выражение для соответствия всем символам хангыль (корейский) и слоговым блокам

Я пытаюсь проверить пользовательский ввод (в Python) и посмотреть, используется ли правильный язык, в данном случае корейский. Давайте возьмем корейское слово для адреса электронной почты: 이메일 주소.

Я могу проверить каждый символ так:

import unicodedata as ud
for chr in u'이메일 주소':
    if 'HANGUL' in ud.name(chr): print "Yep, that's a Korean character."

Но это кажется крайне неэффективным, особенно для длинных текстов. Конечно, я мог бы создать статический словарь, содержащий все блоки корейских слогов, но этот словарь содержал бы около 25 000 символов, и опять же, его было бы неэффективно проверять. Кроме того, мне также нужно решение для японского и китайского языков, которое может содержать еще больше символов.

Поэтому я хотел бы использовать шаблон Regex, охватывающий все символы Unicode для блоков слога хангыль. Но я понятия не имею, есть ли для этого диапазон или где его найти.

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

import re
LATIN_CHARACTERS = re.compile(ur'[\x00-\x7F\x80-\xFF\u0100-\u017F\u0180-\u024F\u1E00-\u1EFF]')

Может ли кто-нибудь перевести это регулярное выражение, чтобы оно соответствовало слоговому блоку корейского хангыля? Или вы можете показать мне таблицу или ссылку для самостоятельного поиска таких диапазонов?

Шаблон, соответствующий китайскому и японскому языкам, также был бы очень полезен. Или одно регулярное выражение для одновременного соответствия всем символам CJK. Мне не нужно было бы различать японцев и корейцев.

Вот библиотека Python для этой задачи, но она работает с невероятно огромными словарями: https://github.com/EliFinkelshteyn/alphabet-detector Я не могу представить, чтобы это было эффективно для большого текста и большого количества пользовательских данных.

Спасибо!


person Simon Steinberger    schedule 03.01.2018    source источник


Ответы (2)


Вы знаете, как Unicode разбивается на блоки и как каждый блок представляет собой непрерывный диапазон кодовых точек? IE, есть гораздо более эффективное решение, чем регулярное выражение.

Существует единый блок кода для Hangul Jamo с дополнительными символами в блок CJK, блок совместимости, слоги хангыль и т. д.

Самый эффективный способ — проверить, находится ли каждый символ в допустимом диапазоне, используя операторы if/then. Вы почти наверняка можете ускорить это, используя C-расширение.

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

def is_hangul_character(char):
    '''Check if character is in the Hangul Jamo block'''

    value = ord(char)
    return value >= 4352 and value <= 4607


def is_hangul(string):
    '''Check if all characters are in the Hangul Jamo block'''

    return all(is_hangul_character(i) for i in string)

Было бы легко расширить это для 8 или около того блоков, содержащих символы хангыль. Нет поиска по таблицам, нет компиляции регулярных выражений. Просто быстрые проверки диапазона на основе блока символа Unicode.

В C это также было бы очень просто (если вы хотите значительно повысить производительность, чтобы соответствовать полностью оптимизированной библиотеке с небольшой работой):

// Return 0 if a character is in Hangul Jamo block, -1 otherwise
int is_hangul_character(char c)
{
    if (c >= 4352 && c <= 4607) {
        return 0;
    }
    return -1;
}


// Return 0 if all characters are in Hangul Jamo block, -1 otherwise
int is_hangul(const char* string, size_t length)
{
    size_t i;
    for (i = 0; i < length; ++i) {
        if (is_hangul_character(string[i]) < 0) {
            return -1;
        }
    }
    return 0;
}

Изменить Беглый взгляд на реализацию CPython показывает, что CPython использует именно этот подход для unicodedata. IE, он эффективен, несмотря на относительную простоту его самостоятельной реализации. Его по-прежнему стоит реализовать, так как вам не нужно выделять какую-либо промежуточную строку или использовать лишнее сравнение строк (что, вероятно, является основной стоимостью модуля unicodedata).

person Alexander Huszagh    schedule 03.01.2018
comment
Так что, по сути, вы говорите: выбрать решение unicodedata? Спасибо за ваш подробный ответ. Я не эксперт ни в Unicode, ни в языках CJK. - person Simon Steinberger; 03.01.2018
comment
@SimonSteinberger Более или менее, и если вас не волнует эффективность, unicodedata будет отлично работать. Но если вам нужно больше эффективности, используя C или Cython, вы можете вернуть логическое значение, а не выделенную строку (как это делает Python). Например, ›95% всех символов хангыль находятся в блоке U+AC00..U+D7AF, который находится в блоке 44,032-55,215. Посмотрите на соответствующие блоки Unicode по ссылке выше, чтобы найти правильные правила, и через полчаса у вас будет все, что вам нужно. - person Alexander Huszagh; 03.01.2018

если вам нужно решение, которое не зависит от соответствия Unicode служебного приложения, для основного блока AC00-D7AF вы можете использовать

(([\352][\260-\277]|[\353\354][\200-\277]|
[\355][\200-\235])[\200-\277]|[\355][\236][\200-\243]) # mawk/gawk -b 

эта плита расширена будет

(\355\236(\200|\201|\202|\203|\204|\205|\206|\207|
\210|\211|\212|\213|\214|\215|\216|\217|\220|\221|
\222|\223|\224|\225|\226|\227|\230|\231|\232|\233|
\234|\235|\236|\237|\240|\241|\242|\243)|
(\352(\260|\261|\262|\263|\264|\265|\266|
\267|\270|\271|\272|\273|\274|\275|\276|\277)|
\355(\200|\201|\202|\203|\204|\205|\206|\207|
\210|\211|\212|\213|\214|\215|\216|\217|\220|
\221|\222|\223|\224|\225|\226|\227|\230|\231|
\232|\233|\234|\235)|(\353|\354)
(\200|\201|\202|\203|\204|\205|\206|\207|\210|
\211|\212|\213|\214|\215|\216|\217|\220|\221|
\222|\223|\224|\225|\226|\227|\230|\231|\232|
\233|\234|\235|\236|\237|\240|\241|\242|\243|
\244|\245|\246|\247|\250|\251|\252|\253|\254|
\255|\256|\257|\260|\261|\262|\263|\264|\265|
\266|\267|\270|\271|\272|\273|\274|\275|\276|
\277))(\200|\201|\202|\203|\204|\205|\206|\207|\210
|\211|\212|\213|\214|\215|\216|\217|\220|\221
|\222|\223|\224|\225|\226|\227|\230|\231|\232
|\233|\234|\235|\236|\237|\240|\241|\242|\243
|\244|\245|\246|\247|\250|\251|\252|\253|\254
|\255|\256|\257|\260|\261|\262|\263|\264|\265
|\266|\267|\270|\271|\272|\273|\274|\275|\276|\277))

если вам нужны дополнительные вещи - джамо, джамо совместимости, форма в кружке, форма в скобках и форма половинной ширины, добавьте это к приведенному выше

либо

 [\341\204\200-\341\207\277
  \343\204\260-\343\206\217
  \352\245\240-\352\245\277
  \355\236\260-\355\237\277
  \343\200\256-\343\200\257
  \343\210\200-\343\210\236
  \343\211\240-\343\211\276
  \357\276\240-\357\276\276
  \357\277\202-\357\277\207
  \357\277\212-\357\277\217
  \357\277\222-\357\277\227
  \357\277\232-\357\277\234]  # gawk unicode-mode only

or

 ((\343\205|\355\237|\341(\204|\205|\206|\207))
(\200|\201|\202|\203|\204|\205|\206|\207|\210|\211
|\212|\213|\214|\215|\216|\217|\220|\221|\222|\223
|\224|\225|\226|\227|\230|\231|\232|\233|\234|\235
|\236|\237|\240|\241|\242|\243|\244|\245|\246|\247
|\250|\251|\252|\253|\254|\255|\256|\257|\260|\261
|\262|\263|\264|\265|\266|\267|\270|\271|\272|\273
|\274|\275|\276|\277)|(\343\204|\355\236)(\260|\261
|\262|\263|\264|\265|\266|\267|\270|\271
|\272|\273|\274|\275|\276|\277)|\343\206(\200|\201
|\202|\203|\204|\205|\206|\207|\210|\211|\212|\213
|\214|\215|\216|\217)|\352\245(\240|\241|\242|\243
|\244|\245|\246|\247|\250|\251|\252|\253|\254|\255
|\256|\257|\260|\261|\262|\263|\264|\265|\266|\267
|\270|\271|\272|\273|\274|\275|\276|\277)
|\343\200\256|\343\200\257|
\343\210(\200|\201|\202|\203|\204|\205|\206|\207
|\210|\211|\212|\213|\214|\215|\216|\217|\220|\221|
\222|\223|\224|\225|\226|\227|\230|\231|\232|\233|
\234|\235|\236)|(\343\211|\357\276)
(\240|\241|\242|\243|\244|\245|\246|\247|\250|\251
|\252|\253|\254|\255|\256|\257|\260|\261|\262|\263
|\264|\265|\266|\267|\270|\271|\272|\273|\274|\275
|\276)|\357\277(\202|\203|\204|\205|\206|\207|\212
|\213|\214|\215|\216|\217|\222|\223|\224|\225|\226
|\227|\232|\233|\234))

если вам нужен только современный джамо, который составляет коллекцию из 11 172 слогов, то он намного чище:

((\341)((\204)[\200-\222]|(\205)[\241-\265]|(\206)[\250-\277]|(\207)[\200-\202]))

или, если вы предпочитаете это без лишних скобок:

(\341(\204[\200-\222]|\205[\241-\265]|\206[\250-\277]|\207[\200-\202]))

PS: я отформатировал здесь только для удобства чтения. между этими восьмеричными кодами нет пробелов или новой строки. это одна непрерывная строка.

лично я бы предпочел сам работать с чистыми регулярными выражениями современной эпохи, но использование этих восьмеричных чисел является для меня необходимым злом, чтобы довести mawk1.3.4 и mawk2-beta до полного соответствия UTF8.

(по крайней мере, с точки зрения lengthC() ordC() substrC() и разделения на уровне символов, но на уровне кодовой точки UC13, плюс NFD-to-NFC только для хангыля.

но ничего особенного, как кластеры графем или двунаправленные тексты)

person RARE Kpop Manifesto    schedule 11.06.2021