La clave de estos problemas de codificación es comprender que, en principio, existen dos conceptos distintos de "cadena" : (1) cadena de caracteres y (2) cadena / matriz de bytes. Esta distinción ha sido mayoritariamente ignorada durante mucho tiempo debido a la ubicuidad histórica de codificaciones con no más de 256 caracteres (ASCII, Latin-1, Windows-1252, Mac OS Roman,…): estas codificaciones mapean un conjunto de caracteres comunes a números entre 0 y 255 (es decir, bytes); el intercambio relativamente limitado de archivos antes de la llegada de la web hizo tolerable esta situación de codificaciones incompatibles, ya que la mayoría de los programas podían ignorar el hecho de que había múltiples codificaciones siempre que produjeran texto que permaneciera en el mismo sistema operativo: tales programas simplemente tratar el texto como bytes (a través de la codificación utilizada por el sistema operativo). La visión moderna y correcta separa adecuadamente estos dos conceptos de cuerdas, basándose en los dos puntos siguientes:
La mayoría de los personajes no están relacionados con las computadoras : uno puede dibujarlos en una pizarra, etc., como por ejemplo بايثون, 中 蟒 y 🐍. Los "caracteres" para máquinas también incluyen "instrucciones de dibujo" como, por ejemplo, espacios, retorno de carro, instrucciones para establecer la dirección de escritura (para árabe, etc.), acentos, etc. El estándar Unicode incluye una lista de caracteres muy grande ; cubre la mayoría de los personajes conocidos.
Por otro lado, las computadoras necesitan representar caracteres abstractos de alguna manera: para esto, usan matrices de bytes (números entre 0 y 255 incluidos), porque su memoria viene en bloques de bytes. El proceso necesario que convierte caracteres en bytes se llama codificación . Por lo tanto, una computadora requiere una codificación para representar caracteres. Cualquier texto presente en su computadora está codificado (hasta que se muestra), ya sea que se envíe a una terminal (que espera caracteres codificados de una manera específica) o se guarde en un archivo. Para que se muestren o "comprendan" correctamente (por ejemplo, el intérprete de Python), los flujos de bytes se decodifican en caracteres. Algunas codificaciones(UTF-8, UTF-16,…) están definidos por Unicode para su lista de caracteres (Unicode define tanto una lista de caracteres como codificaciones para estos caracteres; todavía hay lugares donde uno ve la expresión "codificación Unicode" como un forma de referirse al ubicuo UTF-8, pero esta es una terminología incorrecta, ya que Unicode proporciona múltiples codificaciones).
En resumen, las computadoras necesitan representar internamente caracteres con bytes , y lo hacen a través de dos operaciones:
Codificación : caracteres → bytes
Decodificación : bytes → caracteres
Algunas codificaciones no pueden codificar todos los caracteres (por ejemplo, ASCII), mientras que (algunas) codificaciones Unicode le permiten codificar todos los caracteres Unicode. La codificación tampoco es necesariamente única , porque algunos caracteres se pueden representar directamente o como una combinación (por ejemplo, de un carácter base y de acentos).
Tenga en cuenta que el concepto de nueva línea agrega una capa de complicación , ya que puede estar representado por diferentes caracteres (de control) que dependen del sistema operativo (esta es la razón del modo de lectura de archivos de nueva línea universal de Python ).
Ahora, lo que he llamado "carácter" anteriormente es lo que Unicode llama un " carácter percibido por el usuario ". Un solo carácter percibido por el usuario a veces se puede representar en Unicode combinando partes de caracteres (carácter base, acentos, ...) que se encuentran en diferentes índices de la lista Unicode, que se denominan " puntos de código "; estos puntos de código se pueden combinar para formar un "grupo de grafemas". Unicode conduce así a un tercer concepto de cadena, hecho de una secuencia de puntos de código Unicode, que se encuentra entre cadenas de bytes y caracteres, y que está más cerca de esta última. Los llamaré " cadenas Unicode " (como en Python 2).
Si bien Python puede imprimir cadenas de caracteres (percibidos por el usuario), las cadenas sin bytes de Python son esencialmente secuencias de puntos de código Unicode , no de caracteres percibidos por el usuario. Los valores de los puntos de código son los que se utilizan en la sintaxis de cadenas de Python \u
y \U
Unicode. No deben confundirse con la codificación de un carácter (y no tienen que tener ninguna relación con él: los puntos de código Unicode se pueden codificar de varias formas).
Esto tiene una consecuencia importante: la longitud de una cadena de Python (Unicode) es su número de puntos de código, que no siempre es su número de caracteres percibidos por el usuario : así s = "\u1100\u1161\u11a8"; print(s, "len", len(s))
(Python 3) da a 각 len 3
pesar de s
tener un solo usuario percibido (coreano) carácter (porque está representado con 3 puntos de código, incluso si no es necesario, como se print("\uac01")
muestra). Sin embargo, en muchas circunstancias prácticas, la longitud de una cadena es el número de caracteres percibidos por el usuario, porque Python normalmente almacena muchos caracteres como un único punto de código Unicode.
En Python 2 , las cadenas Unicode se denominan ... "cadenas Unicode" ( unicode
tipo, forma literal u"…"
), mientras que las matrices de bytes son "cadenas" ( str
tipo, donde la matriz de bytes se puede construir, por ejemplo, con cadenas literales "…"
). En Python 3 , las cadenas Unicode se denominan simplemente "cadenas" ( str
tipo, forma literal "…"
), mientras que las matrices de bytes son "bytes" ( bytes
tipo, forma literal b"…"
). Como consecuencia, algo como "🐍"[0]
da un resultado diferente en Python 2 ( '\xf0'
, un byte) y Python 3 ( "🐍"
, el primer y único carácter).
Con estos pocos puntos clave, ¡debería poder comprender la mayoría de las preguntas relacionadas con la codificación!
Normalmente, cuando imprime u"…"
en una terminal , no debería recibir basura: Python conoce la codificación de su terminal. De hecho, puede verificar qué codificación espera el terminal:
% python
Python 2.7.6 (default, Nov 15 2013, 15:20:37)
[GCC 4.2.1 Compatible Apple LLVM 5.0 (clang-500.2.79)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> print sys.stdout.encoding
UTF-8
Si sus caracteres de entrada se pueden codificar con la codificación de la terminal, Python lo hará y enviará los bytes correspondientes a su terminal sin quejarse. El terminal hará todo lo posible para mostrar los caracteres después de decodificar los bytes de entrada (en el peor de los casos, la fuente del terminal no tiene algunos de los caracteres y en su lugar imprimirá algún tipo de espacio en blanco).
Si sus caracteres de entrada no se pueden codificar con la codificación del terminal, significa que el terminal no está configurado para mostrar estos caracteres. Python se quejará (en Python con un UnicodeEncodeError
dado que la cadena de caracteres no se puede codificar de una manera que se adapte a su terminal). La única solución posible es usar una terminal que pueda mostrar los caracteres (ya sea configurando la terminal para que acepte una codificación que pueda representar sus caracteres, o usando un programa de terminal diferente). Esto es importante cuando distribuye programas que se pueden utilizar en diferentes entornos: los mensajes que imprima deben poder representarse en el terminal del usuario. A veces, por lo tanto, es mejor ceñirse a cadenas que solo contienen caracteres ASCII.
Sin embargo, cuando redirige o canaliza la salida de su programa, generalmente no es posible saber cuál es la codificación de entrada del programa receptor, y el código anterior devuelve alguna codificación predeterminada: Ninguna (Python 2.7) o UTF-8 ( Python 3):
% python2.7 -c "import sys; print sys.stdout.encoding" | cat
None
% python3.4 -c "import sys; print(sys.stdout.encoding)" | cat
UTF-8
Sin embargo, la codificación de stdin, stdout y stderr se puede configurar a través de la PYTHONIOENCODING
variable de entorno, si es necesario:
% PYTHONIOENCODING=UTF-8 python2.7 -c "import sys; print sys.stdout.encoding" | cat
UTF-8
Si la impresión en un terminal no produce lo que espera, puede verificar que la codificación UTF-8 que ingresó manualmente sea correcta; por ejemplo, su primer carácter ( \u001A
) no se puede imprimir, si no me equivoco .
En http://wiki.python.org/moin/PrintFails , puede encontrar una solución como la siguiente, para Python 2.x:
import codecs
import locale
import sys
# Wrap sys.stdout into a StreamWriter to allow writing unicode.
sys.stdout = codecs.getwriter(locale.getpreferredencoding())(sys.stdout)
uni = u"\u001A\u0BC3\u1451\U0001D10C"
print uni
Para Python 3, puede consultar una de las preguntas que se hicieron anteriormente en StackOverflow.