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