Подтвердить что ты не робот

Правильно извлеките Emojis из строки Unicode

Я работаю в Python 2, и у меня есть строка, содержащая emojis, а также другие символы Unicode. Мне нужно преобразовать его в список, где каждая запись в списке является единственным символом /emoji.

x = u'😘😘xyz😊😊'
char_list = [c for c in x]

Требуемый результат:

['😘', '😘', 'x', 'y', 'z', '😊', '😊']

Фактический вывод:

[u'\ud83d', u'\ude18', u'\ud83d', u'\ude18', u'x', u'y', u'z', u'\ud83d', u'\ude0a', u'\ud83d', u'\ude0a']

Как я могу достичь желаемого результата?

4b9b3361

Ответ 1

Прежде всего, в Python2 вам нужно использовать строки Unicode (u'<...>') для символов Unicode, которые будут отображаться как символы Unicode. И правильное кодирование источника, если вы хотите использовать сами символы, а не представление \UXXXXXXXX в исходном коде.

Теперь, согласно Python: получение правильной длины строки, когда она содержит суррогатные пары и Python возвращает длину 2 для одного Unicode символьная строка, в Python2 "узкие" сборки (с sys.maxunicode==65535), 32-разрядные символы Юникода представлены как суррогатные пары, и это непрозрачно для строковых функций. Это было зафиксировано только в 3.3 (PEP0393).

Простейшее разрешение (за исключением перехода на 3.3+) заключается в компиляции "широкой" сборки Python из источника, как указано в третьей ссылке. В ней символы Unicode - все 4 байта ( таким образом, является потенциальным всплеском памяти), но если вам нужно регулярно обрабатывать широкие символы Unicode, это, вероятно, приемлемая цена.

Решение для "узкой" сборки - , чтобы создать собственный набор строковых функций (len, slice; возможно, в качестве подкласса unicode), который будет определять суррогатные пары и обрабатывать их как один символ. Я не мог легко найти существующую (что странно), но писать ее не так сложно:

  • согласно UTF-16 # U + 10000 to U + 10FFFF - Википедия,
    • 1-й символ (высокий суррогат) находится в диапазоне 0xD800..0xDBFF
    • 2-й символ (низкий суррогат) - в диапазоне 0xDC00..0xDFFF
    • эти диапазоны зарезервированы и, следовательно, не могут встречаться как обычные символы

Итак, вот код для обнаружения суррогатной пары:

def is_surrogate(s,i):
    if 0xD800 <= ord(s[i]) <= 0xDBFF:
        try:
            l = s[i+1]
        except IndexError:
            return False
        if 0xDC00 <= ord(l) <= 0xDFFF:
            return True
        else:
            raise ValueError("Illegal UTF-16 sequence: %r" % s[i:i+2])
    else:
        return False

И функция, которая возвращает простой срез:

def slice(s,start,end):
    l=len(s)
    i=0
    while i<start and i<l:
        if is_surrogate(s,i):
            start+=1
            end+=1
            i+=1
        i+=1
    while i<end and i<l:
        if is_surrogate(s,i):
            end+=1
            i+=1
        i+=1
    return s[start:end]

Здесь цена, которую вы платите, - это производительность, так как эти функции намного медленнее, чем встроенные:

>>> ux=u"a"*5000+u"\U00100000"*30000+u"b"*50000
>>> timeit.timeit('slice(ux,10000,100000)','from __main__ import slice,ux',number=1000)
46.44128203392029    #msec
>>> timeit.timeit('ux[10000:100000]','from __main__ import slice,ux',number=1000000)
8.814016103744507    #usec

Ответ 2

Я бы использовал библиотеку uniseg (pip install uniseg):

# -*- coding: utf-8 -*-
from uniseg import graphemecluster as gc

print list(gc.grapheme_clusters(u'😘😘xyz😊😊'))

выводит [u'\U0001f618', u'\U0001f618', u'x', u'y', u'z', u'\U0001f60a', u'\U0001f60a'] и

[x.encode('utf-8') for x in gc.grapheme_clusters(u'😘😘xyz😊😊'))]

предоставит список символов как кодированные строки UTF-8.