Semántica Unicode específica de la plataforma en Python 2.7

Ubuntu 11.10:

$ python Python 2.7.2+ (default, Oct 4 2011, 20:03:08) [GCC 4.6.1] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> x = u'\U0001f44d' >>> len(x) 1 >>> ord(x[0]) 128077 

Windows 7:

 Python 2.7.2 (default, Jun 12 2011, 15:08:59) [MSC v.1500 32 bit (Intel)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> x = u'\U0001f44d' >>> len(x) 2 >>> ord(x[0]) 55357 

Mi experiencia con Ubuntu es con el intérprete predeterminado en la distribución. Para Windows 7 descargué e instalé la versión recomendada enlazada desde python.org. Yo tampoco compilé ninguno de ellos.

La naturaleza de la diferencia es clara para mí. (En Ubuntu, la cadena es una secuencia de puntos de código; en Windows 7, una secuencia de unidades de código UTF-16). Mis preguntas son:

  • ¿Por qué estoy observando esta diferencia en el comportamiento? ¿Se debe a cómo se construye el intérprete oa una diferencia en las bibliotecas dependientes del sistema?
  • ¿Hay alguna forma de configurar el comportamiento del intérprete de Windows 7 para que coincida con el de Ubuntu, que puedo hacer dentro de Eclipse PyDev (mi objective)?
  • Si tengo que reconstruir, ¿hay algún intérprete de Windows 7 creado previamente que se comporte como Ubuntu desde una fuente confiable?
  • ¿Hay alguna solución alternativa a este problema además de contar manualmente sustitutos en cadenas unicode solo en Windows (blech)?
  • ¿Esto justifica un informe de error? ¿Existe alguna posibilidad de que un informe de este tipo de error se aborde en 2.7?

En Ubuntu, tienes una comstackción Python “ancha” donde las cadenas son UTF-32 / UCS-4. Desafortunadamente, esto no está (todavía) disponible para Windows.

Las comstackciones de Windows serán limitadas por un tiempo debido al hecho de que ha habido pocas solicitudes de caracteres anchos, que en su mayoría son de progtwigdores con la capacidad de comprar su propio Python y la misma Windows está fuertemente sesgada hacia los caracteres de 16 bits. .

Python 3.3 tendrá una representación de cadena flexible , en la que no tendrá que preocuparse por si las cadenas Unicode usan unidades de código de 16 bits o de 32 bits.

Hasta entonces, puede obtener los puntos de código de una cadena UTF-16 con

 def code_points(text): utf32 = text.encode('UTF-32LE') return struct.unpack('<{}I'.format(len(utf32) // 4), utf32) 

gran pregunta Me caí en este agujero de conejo recientemente yo mismo.

La respuesta de @ dan04 me inspiró a expandirla en una subclase de unicode que proporciona indexación, segmentación y len() consistentes en comstackciones de Python 2 estrechas y anchas:

 class WideUnicode(unicode): """String class with consistent indexing, slicing, len() on both narrow and wide Python.""" def __init__(self, *args, **kwargs): super(WideUnicode, self).__init__(*args, **kwargs) # use UTF-32LE to avoid a byte order marker at the beginning of the string self.__utf32le = unicode(self).encode('utf-32le') def __len__(self): return len(self.__utf32le) / 4 def __getitem__(self, key): length = len(self) if isinstance(key, int): if key >= length: raise IndexError() key = slice(key, key + 1) if key.stop is None: key.stop = length assert key.step is None return WideUnicode(self.__utf32le[key.start * 4:key.stop * 4] .decode('utf-32le')) def __getslice__(self, i, j): return self.__getitem__(slice(i, j)) 

Abrir código fuente aquí , dominio público. ejemplo de uso:

 text = WideUnicode(obj.text) for tag in obj.tags: text = WideUnicode(text[:start] + tag.text + text[end:]) 

( simplificado de este uso. )

gracias @ dan04!

Necesitaba principalmente probar la longitud con precisión. Por lo tanto, esta función que devuelve correctamente la longitud del punto de código de cualquier cadena unicode , ya sea que el intérprete sea estrecho o ancho. Si los datos utilizan dos literales sustitutos en lugar de un único punto de código de estilo \U en un intérprete de construcción amplia, la longitud del punto de código devuelto se considerará siempre que los sustitutos se usen “correctamente”, es decir, como un intérprete de construcción limitada Los usaría.

 invoke = lambda f: f() # trick borrowed from Node.js @invoke def ulen(): testlength = len(u'\U00010000') assert (testlength == 1) or (testlength == 2) if testlength == 1: # "wide" interpreters def closure(data): u'returns the number of Unicode code points in a unicode string' return len(data.encode('UTF-16BE').decode('UTF-16BE')) else: # "narrow" interpreters def filt(c): ordc = ord(c) return (ordc >= 55296) and (ordc < 56320) def closure(data): u'returns the number of Unicode code points in a unicode string' return len(data) - len(filter(filt, data)) return closure # ulen() body is therefore different on narrow vs wide builds 

Caso de prueba, pasa en construcciones estrechas y anchas:

 class TestUlen(TestCase): def test_ulen(self): self.assertEquals(ulen(u'\ud83d\udc4d'), 1) self.assertEquals(ulen(u'\U0001F44D'), 1)