Как сделать Python split() на языках (например, китайском), которые не используют пробелы в качестве разделителя слов?
Я хочу разбить предложение на список слов.
Для английского и европейского языков это легко, просто используйте split()
>>> "This is a sentence.".split()
['This', 'is', 'a', 'sentence.']
Но мне также нужно иметь дело с предложениями на таких языках, как китайский, которые не используют пробелы в качестве разделителя слов.
>>> u"这是一个句子".split()
[u'\u8fd9\u662f\u4e00\u4e2a\u53e5\u5b50']
Очевидно, что это не работает.
Как расколоть такое предложение в список слов?
UPDATE:
До сих пор ответы, похоже, предполагают, что для этого требуются методы обработки естественного языка и что границы слов на китайском языке неоднозначны. Я не уверен, что понимаю, почему. Языковые границы на китайском языке кажутся мне очень определенными. Каждое китайское слово/символ имеет соответствующий юникод и отображается на экране в виде отдельного слова/символа.
Итак, откуда возникает двусмысленность. Как вы можете видеть на моем выходе на консоль Python, у Python нет проблем, говоря, что мое примерное предложение составлено из 5 символов:
这 - u8fd9
是 - u662f
一 - u4e00
个 - u4e2a
句 - u53e5
子 - u5b50
Таким образом, очевидно, что у Python нет проблем, говорящих о границах слов/символов. Мне просто нужны эти слова/символы в списке.
Ответы
Ответ 1
просто предостережение: используя list( '...' )
(в Py3, что u'...'
для Py2), в общем случае не даст вам символов строки юникода; скорее, это, скорее всего, приведет к ряду 16-битных кодовых точек. это справедливо для всех "узких" CPython-сборок, на которые приходится подавляющее большинство установок python.
когда в 1990-х годах впервые был предложен уникод, было высказано предположение, что 16 бит будут более чем достаточными, чтобы охватить все потребности универсального кодирования текста, поскольку он позволил перейти от 128 кодовых точек (7 бит) и 256 кодовых точек ( 8 бит) до колоссальных 65'536 кодовых точек. однако вскоре стало очевидно, что это было принятие желаемого за действительное; сегодня около 100 000 кодовых точек определены в unicode версии 5.2, а еще тысячи ожидаются для включения. для того, чтобы это стало возможным, юникод должен был перейти от 16 до (концептуально) 32 бит (хотя он не в полной мере использует 32-битное адресное пространство).
чтобы поддерживать совместимость с программным обеспечением, основанным на предположении, что unicode все еще 16 бит, были разработаны так называемые суррогатные пары, где два 16-битных кодовых пункта из специально определенных блоков используются для выражения кодовых точек за пределами 65'536, то есть, помимо того, что unicode вызывает "базовый многоязычный план" или BMP, и которые в шутку называются "астральными" плоскостями этой кодировки, за их относительную неуловимость и постоянную головную боль, которые они предлагают людям, работающим в области обработки текста и кодирование.
теперь, в то время как узкие CPython в некоторых случаях довольно прозрачно разбираются с суррогатными парами, в других случаях они все равно не будут поступать правильно, а разделение строк является одним из тех более неприятных случаев. в узкой сборке python list( 'abc大𧰼def' )
(или list( 'abc\u5927\U00027C3Cdef' )
при написании с экранами) приведет к ['a', 'b', 'c', '大', '\ud85f', '\udc3c', 'd', 'e', 'f']
, при этом '\ud85f', '\udc3c'
будет суррогатной парой. Кстати, '\ud85f\udc3c'
- это то, что ожидает JSON-стандарт, чтобы представить U-27C3C
. любой из этих кодовых точек бесполезен сам по себе; хорошо сформированная строка юникода может иметь только пары суррогатов.
так что вы хотите разбить строку на символы:
from re import compile as _Re
_unicode_chr_splitter = _Re( '(?s)((?:[\ud800-\udbff][\udc00-\udfff])|.)' ).split
def split_unicode_chrs( text ):
return [ chr for chr in _unicode_chr_splitter( text ) if chr ]
который правильно возвращает ['a', 'b', 'c', '大', '𧰼', 'd', 'e', 'f']
(обратите внимание: вы, вероятно, можете переписать регулярное выражение, чтобы фильтрация пустых строк стала ненужной).
Если все, что вы хотите сделать, это расщепление текста на китайские символы, вы бы в значительной степени сделали это. не знаю, что такое концепция OP слова "слово", но для меня 这 是 一个 句子 можно разделить на 这 |是 |一 |个 |句子, а также 这 是 |一个 |句子, в зависимости от вашей точки зрения. однако все, что выходит за рамки понятий (возможно составленных) символов и классов символов (символы против пробелов против букв и т.д.), выходит далеко за рамки того, что встроено в юникод и питон; для этого вам понадобится обработка естественного языка. позвольте мне заметить, что, хотя ваш пример 'yes the United Nations can!'.split()
успешно демонстрирует, что метод split делает что-то полезное для большого количества данных, он не анализирует английский текст в словах правильно: он не распознает United Nations
как одно слово, а ложно предполагает, что can!
- это слово, которое явно нет. этот метод дает как ложные срабатывания, так и ложные негативы. в зависимости от ваших данных и того, что вы намереваетесь выполнить, это может быть или не быть тем, что вы хотите.
Ответ 2
Вы можете сделать это, но не со стандартными библиотечными функциями. И регулярные выражения вам тоже не помогут.
Задача, которую вы описываете, является частью поля, называемого Обработка естественного языка (NLP). Проделана большая работа по разделению китайских слов на границах слов. Я бы предположил, что вы используете одно из этих существующих решений вместо того, чтобы пытаться катиться самостоятельно.
Откуда возникает двусмысленность?
То, что вы там указали, есть китайские иероглифы. Они примерно аналогичны буквам или слогам на английском языке (но не совсем так же, как NullUserException указывает на комментарий). Нет никакой двусмысленности в отношении границ символов - это очень хорошо определено. Но вы спрашивали не о границах символов, а о границах слов. Китайские слова могут состоять из более чем одного символа.
Если вы хотите найти символы, это очень просто и не требует библиотеки NLP. Просто декодируйте сообщение в строку unicode (если это еще не сделано), затем преобразуйте строку unicode в список, используя вызов встроенной функции list
. Это даст вам список символов в строке. Для вашего конкретного примера:
>>> list(u"这是一个句子")
Ответ 3
Языки, подобные китайскому, имеют очень плавное определение слова. Например. Один смысл ma
- "лошадь". Один смысл shang
- "выше" или "сверху". Соединение - это "машанг", что означает буквально "верхом", но его образно означает "немедленно". Вам нужен очень хороший словарь с составами в нем, и поиск словаря требует подхода с самым длинным соответствием. Уплотнение распространено на немецком языке (известный пример - это что-то вроде "супруга директора паровой компании" Дунай ", выражаемая как одно слово), тюркские языки, финский и мадьяр - эти языки имеют очень длинные слова, многие из которых не будут найдены в словарь и нужно сломаться, чтобы понять их.
Ваша проблема - это лингвистика, не имеющая ничего общего с Python.
Ответ 4
Хорошо, я понял это.
То, что мне нужно, можно выполнить, просто используя list():
>>> list(u"这是一个句子")
[u'\u8fd9', u'\u662f', u'\u4e00', u'\u4e2a', u'\u53e5', u'\u5b50']
Спасибо за все ваши входы.
Ответ 5
Лучший инструмент токенизатора для китайского языка - pynlpir.
import pynlpir
pynlpir.open()
mystring = "你汉语说的很好!"
tokenized_string = pynlpir.segment(mystring, pos_tagging=False)
>>> tokenized_string
['你', '汉语', '说', '的', '很', '好', '!']
Помните о том, что у pynlpir есть пресловутая, но легко решаемая проблема с лицензированием, благодаря которой вы можете найти множество решений в Интернете.
Вам просто нужно заменить файл NLPIR.user в папке NLPIR, загрузив действительную лицензию из этого хранилища, и перезапустить среду.
Ответ 6
Это частично возможно с японцами, где у вас обычно есть разные классы символов в начале и в конце слова, но есть целые научные статьи по этому вопросу для китайцев. У меня есть регулярное выражение для разделения слов на японском языке, если вам интересно: http://hg.hatta-wiki.org/hatta-dev/file/cd21122e2c63/hatta/search.py#l19
Ответ 7
Попробуйте следующее: http://code.google.com/p/pymmseg-cpp/
Ответ 8
list() - это ответ на предложение только для Китая. Для этих гибридных английских/китайских в большинстве случаев. Он ответил на hybrid-split, просто скопируйте ответ из Winter, как показано ниже.
def spliteKeyWord(str):
regex = r"[\u4e00-\ufaff]|[0-9]+|[a-zA-Z]+\'*[a-z]*"
matches = re.findall(regex, str, re.UNICODE)
return matches
Ответ 9
если строка длиннее 30, возьмите 27 символов и добавьте "..." в конце
в противном случае верните str
str='中文2018-2020年一区6、8、10、12号楼_「工程建设文档102332号」'
result = len(list(str)) >= 30 and ''.join(list(str)[:27]) + '...' or str