Python: есть ли способ выполнить этот «гибридный» split () для многоязычных (например, китайских и английских) строк? - PullRequest
8 голосов
/ 27 сентября 2010

У меня есть многоязычные строки, состоящие из языков, которые используют пробел в качестве разделителя слов (английский, французский и т. Д.), И языков, которые не используют (китайский, японский, корейский).

Учитывая такую ​​строку, я хочу разделить часть на английском / французском / etc на слова, используя пробел в качестве разделителя, и разделить часть на китайском / японском / корейском языке на отдельные символы.

И я хочу поместить все эти отдельные компоненты в список.

Некоторые примеры, вероятно, прояснили бы это:

Случай 1 : только английская строка. Этот случай прост:

>>> "I love Python".split()
['I', 'love', 'Python']

Случай 2 : только китайская строка:

>>> list(u"我爱蟒蛇")
[u'\u6211', u'\u7231', u'\u87d2', u'\u86c7']

В этом случае я могу превратить строку в список китайских символов. Но в списке я получаю Unicode-представления:

[u'\u6211', u'\u7231', u'\u87d2', u'\u86c7']

Как мне заставить его отображать реальные символы вместо юникода? Что-то вроде:

['我', '爱', '蟒', '蛇']

??

Дело 3 : смесь английского и китайского:

Я хочу включить строку ввода, например

"我爱Python"

и превращает его в такой список:

['我', '爱', 'Python']

Можно ли сделать что-то подобное?

Ответы [ 5 ]

6 голосов
/ 27 сентября 2010

Я тоже решил показать подход регулярных выражений.Мне это не кажется правильным, но это в основном потому, что все странные особенности языка, которые я видел, заставляют меня беспокоиться о том, что регулярное выражение может быть недостаточно гибким для всех из них - но вам, возможно, не понадобитсятого, что.(Другими словами - чрезмерный дизайн.)

# -*- coding: utf-8 -*-
import re
def group_words(s):
    regex = []

    # Match a whole word:
    regex += [ur'\w+']

    # Match a single CJK character:
    regex += [ur'[\u4e00-\ufaff]']

    # Match one of anything else, except for spaces:
    regex += [ur'[^\s]']

    regex = "|".join(regex)
    r = re.compile(regex)

    return r.findall(s)

if __name__ == "__main__":
    print group_words(u"Testing English text")
    print group_words(u"我爱蟒蛇")
    print group_words(u"Testing English text我爱蟒蛇")

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

2 голосов
/ 04 июля 2017

В Python 3 он также разделяет число, если вам нужно.

def spliteKeyWord(str):
    regex = r"[\u4e00-\ufaff]|[0-9]+|[a-zA-Z]+\'*[a-z]*"
    matches = re.findall(regex, str, re.UNICODE)
    return matches

print(spliteKeyWord("Testing English text我爱Python123"))

=> ['Testing', 'English', 'text', '我', '爱', 'Python',' 123 ']

2 голосов
/ 27 сентября 2010

Форматирование списка показывает repr его компонентов. Если вы хотите просмотреть строки, а не экранировать их, вам нужно отформатировать их самостоятельно. (repr должен не экранировать эти символы; repr(u'我') должен возвращать "u'我'", а не "u'\\u6211'. Очевидно, это происходит в Python 3; только 2.x застрял с англо-ориентированным экранирование для строк Unicode.)

Основной алгоритм, который вы можете использовать, - это присвоение класса символов каждому символу, а затем группировка букв по классам. Стартовый код указан ниже.

Я не использовал doctest для этого, потому что столкнулся с некоторыми странными проблемами кодирования, которые я не хочу рассматривать (выходит за рамки). Вам нужно будет реализовать правильную функцию группировки.

Обратите внимание, что если вы используете это для переноса слов, существуют другие языковые соображения. Например, вы не хотите разбивать на неразрывные пробелы; Вы хотите разбить на дефисы; для японцев ты не хочешь разделяться на части き ゅ; и т. д.

# -*- coding: utf-8 -*-
import itertools, unicodedata

def group_words(s):
    # This is a closure for key(), encapsulated in an array to work around
    # 2.x's lack of the nonlocal keyword.
    sequence = [0x10000000]

    def key(part):
        val = ord(part)
        if part.isspace():
            return 0

        # This is incorrect, but serves this example; finding a more
        # accurate categorization of characters is up to the user.
        asian = unicodedata.category(part) == "Lo"
        if asian:
            # Never group asian characters, by returning a unique value for each one.
            sequence[0] += 1
            return sequence[0]

        return 2

    result = []
    for key, group in itertools.groupby(s, key):
        # Discard groups of whitespace.
        if key == 0:
            continue

        str = "".join(group)
        result.append(str)

    return result

if __name__ == "__main__":
    print group_words(u"Testing English text")
    print group_words(u"我爱蟒蛇")
    print group_words(u"Testing English text我爱蟒蛇")
0 голосов
/ 01 августа 2018

Для python3.7 работает следующее:

import re
def group_words(s):
    return re.findall(u'[\u4e00-\u9fff]|[a-zA-Z0-9]+', s)


if __name__ == "__main__":
    print(group_words(u"Testing English text"))
    print(group_words(u"我爱蟒蛇"))
    print(group_words(u"Testing English text我爱蟒蛇"))

['Testing', 'English', 'text']
['我', '爱', '蟒', '蛇']
['Testing', 'English', 'text', '我', '爱', '蟒', '蛇']

Почему-то я не могу адаптировать ответ Гленна Мейнарда на python3.

0 голосов
/ 11 июня 2015

Модифицированное решение Гленна для удаления символов и работы с русским, французским и т. Д. Алфавитами:

def rec_group_words():
    regex = []

    # Match a whole word:
    regex += [r'[A-za-z0-9\xc0-\xff]+']

    # Match a single CJK character:
    regex += [r'[\u4e00-\ufaff]']

    regex = "|".join(regex)
    return re.compile(regex)
...