¿Cómo iterar sobre los caracteres Unicode en Python 3?

Necesito pasar por una cadena de Python un carácter a la vez, pero un simple “for” loop me da unidades de código UTF-16 en su lugar:

str = "abc\u20ac\U00010302\U0010fffd" for ch in str: code = ord(ch) print("U+{:04X}".format(code)) 

Que imprime

 U+0061 U+0062 U+0063 U+20AC U+D800 U+DF02 U+DBFF U+DFFD 

cuando lo que queria era

 U+0061 U+0062 U+0063 U+20AC U+10302 U+10FFFD 

¿Hay alguna forma de que Python me dé la secuencia de puntos de código Unicode, independientemente de cómo la cadena esté realmente codificada bajo el capó? Estoy probando en Windows aquí, pero necesito un código que funcione en cualquier lugar. Solo necesita trabajar en Python 3, no me importa Python 2.x.

Lo mejor que he podido encontrar hasta ahora es esto:

 import codecs str = "abc\u20ac\U00010302\U0010fffd" bytestr, _ = codecs.getencoder("utf_32_be")(str) for i in range(0, len(bytestr), 4): code = 0 for b in bytestr[i:i + 4]: code = (code << 8) + b print("U+{:04X}".format(code)) 

Pero espero que haya una forma más sencilla.

(Con una pista de cuatro, una terminología de Unicode precisa será golpeada en la cabeza de manera implacable. Creo que he dejado en claro lo que estoy buscando aquí, por favor, no desperdicies el espacio con “pero UTF-16 es Unicode técnicamente también “tipo de argumentos.)

Related of "¿Cómo iterar sobre los caracteres Unicode en Python 3?"

En Python 3.2.1 con construcción Unicode estrecha:

 PythonWin 3.2.1 (default, Jul 10 2011, 21:51:15) [MSC v.1500 32 bit (Intel)] on win32. Portions Copyright 1994-2008 Mark Hammond - see 'Help/About PythonWin' for further copyright information. >>> import sys >>> sys.maxunicode 65535 

Lo que has descubierto (encoding UTF-16):

 >>> s = "abc\u20ac\U00010302\U0010fffd" >>> len(s) 8 >>> for c in s: ... print('U+{:04X}'.format(ord(c))) ... U+0061 U+0062 U+0063 U+20AC U+D800 U+DF02 U+DBFF U+DFFD 

Una forma de evitarlo:

 >>> import struct >>> s=s.encode('utf-32-be') >>> struct.unpack('>{}L'.format(len(s)//4),s) (97, 98, 99, 8364, 66306, 1114109) >>> for i in struct.unpack('>{}L'.format(len(s)//4),s): ... print('U+{:04X}'.format(i)) ... U+0061 U+0062 U+0063 U+20AC U+10302 U+10FFFD 

Actualización para Python 3.3:

Ahora funciona de la manera que el OP espera:

 >>> s = "abc\u20ac\U00010302\U0010fffd" >>> len(s) 6 >>> for c in s: ... print('U+{:04X}'.format(ord(c))) ... U+0061 U+0062 U+0063 U+20AC U+10302 U+10FFFD 

Python normalmente almacena los valores Unicode internamente como UCS2. La representación UTF-16 del carácter UTF-32 \ U00010302 es \ UD800 \ UDF02, es por eso que obtuvo ese resultado.

Dicho esto, hay algunas comstackciones de python que usan UCS4, pero estas comstackciones no son compatibles entre sí.

Echa un vistazo aquí .

Py_UNICODE Este tipo representa el tipo de almacenamiento que Python usa internamente como base para mantener los ordinales de Unicode. Las comstackciones predeterminadas de Python utilizan un tipo de 16 bits para Py_UNICODE y almacenan valores Unicode internamente como UCS2. También es posible crear una versión UCS4 de Python (las distribuciones más recientes de Linux vienen con versiones UCS4 de Python). Estas comstackciones luego usan un tipo de 32 bits para Py_UNICODE y almacenan datos Unicode internamente como UCS4. En plataformas donde wchar_t está disponible y es compatible con la variante de comstackción elegida de Python Unicode, Py_UNICODE es un alias typedef para wchar_t para mejorar la compatibilidad de la plataforma nativa. En todas las demás plataformas, Py_UNICODE es un alias typedef para short sin signo (UCS2) o long sin signo (UCS4).

Si crea la cadena como un objeto Unicode, debería poder separar un carácter a la vez automáticamente. P.ej:

Python 2.6:

 s = u"abc\u20ac\U00010302\U0010fffd" # note u in front! for c in s: print "U+%04x" % ord(c) 

Recibí:

 U+0061 U+0062 U+0063 U+20ac U+10302 U+10fffd 

Python 3.2:

 s = "abc\u20ac\U00010302\U0010fffd" for c in s: print ("U+%04x" % ord(c)) 

Funcionó para mí:

 U+0061 U+0062 U+0063 U+20ac U+10302 U+10fffd 

Además, encontré este enlace que explica que el comportamiento funciona correctamente. Si la cadena proviene de un archivo, etc., es probable que primero deba decodificarse.

Actualización :

He encontrado una explicación perspicaz aquí . El tamaño de representación interno de Unicode es una opción de tiempo de comstackción, y si trabaja con caracteres “anchos” fuera del plano de 16 bits, tendrá que crear Python para eliminar la limitación, o usar una de las soluciones en esta página. Aparentemente, muchas distribuciones de Linux hacen esto por ti ya que encontré arriba.