python unicode python-2.x emoji

python - Extrae correctamente Emojis de una cadena Unicode



python-2.x (2)

Estoy trabajando en Python 2 y tengo una cadena que contiene emojis y otros caracteres Unicode. Necesito convertirlo a una lista donde cada entrada de la lista sea un solo car谩cter / emoji.

x = u''馃槝馃槝xyz馃槉馃槉'' char_list = [c for c in x]

El resultado deseado es:

[''馃槝'', ''馃槝'', ''x'', ''y'', ''z'', ''馃槉'', ''馃槉'']

La salida real es:

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

驴C贸mo puedo lograr el resultado deseado?


Utilizar铆a la biblioteca uniseg ( pip install uniseg ):

# -*- coding: utf-8 -*- from uniseg import graphemecluster as gc print list(gc.grapheme_clusters(u''馃槝馃槝xyz馃槉馃槉''))

salidas [u''/U0001f618'', u''/U0001f618'', u''x'', u''y'', u''z'', u''/U0001f60a'', u''/U0001f60a''] , y

[x.encode(''utf-8'') for x in gc.grapheme_clusters(u''馃槝馃槝xyz馃槉馃槉''))]

proporcionar谩 la lista de caracteres como cadenas codificadas UTF-8.


En primer lugar, en Python2, debe utilizar cadenas Unicode ( u''<...>'' ) para que los caracteres Unicode se vean como caracteres Unicode. Y corrija la codificaci贸n de origen si desea usar los caracteres en s铆 mismos en lugar de la representaci贸n /UXXXXXXXX en el c贸digo fuente.

Ahora, seg煤n Python: obtener la longitud de cadena correcta cuando contiene pares de sustituci贸n y Python devuelve una longitud de 2 para cadena de caracteres Unicode , en las versiones "estrechas" de sys.maxunicode==65535 (con sys.maxunicode==65535 ), se representan caracteres Unicode de 32 bits como pares de sustituci贸n , y esto no es transparente para las funciones de cadena. Esto solo se ha corregido en 3.3 ( PEP0393 ).

La resoluci贸n m谩s simple (salvo para migrar a 3.3+) es compilar una compilaci贸n "amplia" de Python desde la fuente como se indica en el tercer enlace. En 茅l, los caracteres Unicode son todos de 4 bytes (por lo tanto, son un potencial hog de la memoria), pero si necesita manejar rutinariamente caracteres anchos Unicode, este es probablemente un precio aceptable.

La soluci贸n para una compilaci贸n "estrecha" es hacer un conjunto personalizado de funciones de cadena ( len , slice , tal vez como una subclase de unicode ) que detecte los pares de sustituci贸n y los maneje como un solo car谩cter. No pude encontrar f谩cilmente uno existente (lo cual es extra帽o), pero no es demasiado dif铆cil de escribir:

  • seg煤n UTF-16 # U + 10000 a U + 10FFFF - Wikipedia ,
    • el primer car谩cter (sustituto alto) est谩 en el rango 0xD800..0xDBFF
    • el segundo car谩cter (sustituto bajo) - en el rango 0xDC00..0xDFFF
    • estos rangos est谩n reservados y por lo tanto no pueden ocurrir como caracteres regulares

As铆 que aqu铆 est谩 el c贸digo para detectar un par suplente:

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

Y una funci贸n que devuelve un simple segmento:

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]

Aqu铆, el precio que paga es el rendimiento, ya que estas funciones son mucho m谩s lentas que las integradas:

>>> 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