¿Cuál es la diferencia entre una cadena y una cadena de bytes?

210

Estoy trabajando con una biblioteca que devuelve una cadena de bytes y necesito convertir esto en una cadena.

Aunque no estoy seguro de cuál es la diferencia, si es que la hay.

Sheldon
fuente

Respuestas:

261

Suponiendo Python 3 (en Python 2, esta diferencia está un poco menos definida): una cadena es una secuencia de caracteres, es decir, puntos de código unicode ; Estos son conceptos abstractos y no pueden almacenarse directamente en el disco. Una cadena de bytes es una secuencia de, como era de esperar, bytes, cosas que se pueden almacenar en el disco. El mapeo entre ellos es una codificación : hay muchos de estos (e infinitos son posibles) y necesita saber cuál se aplica en el caso particular para realizar la conversión, ya que una codificación diferente puede mapear los mismos bytes a una cadena diferente:

>>> b'\xcf\x84o\xcf\x81\xce\xbdo\xcf\x82'.decode('utf-16')
'蓏콯캁澽苏'
>>> b'\xcf\x84o\xcf\x81\xce\xbdo\xcf\x82'.decode('utf-8')
'τoρνoς'

Una vez que sepa cuál usar, puede usar el .decode()método de la cadena de bytes para obtener la cadena de caracteres correcta como se indicó anteriormente. Para completar, el .encode()método de una cadena de caracteres va en sentido contrario:

>>> 'τoρνoς'.encode('utf-8')
b'\xcf\x84o\xcf\x81\xce\xbdo\xcf\x82'
lvc
fuente
77
Para aclarar a los usuarios de Python 2: el strtipo es el mismo que el bytestipo; esta respuesta es comparar de manera equivalente el unicodetipo (no existe en Python 3) con el strtipo.
craymichael
3
@KshitijSaraogi eso tampoco es del todo cierto; toda esa oración fue editada y es un poco desafortunada. La representación en memoria de los strobjetos de Python 3 no es accesible o relevante desde el lado de Python; La estructura de datos es solo una secuencia de puntos de código. Bajo PEP 393 , la codificación interna exacta es una de Latin-1, UCS2 o UCS4, y una representación utf-8 puede almacenarse en caché después de que se solicite por primera vez, pero incluso el código C no se recomienda confiar en estos detalles internos.
lvc
1
Si no pueden almacenarse directamente en el disco, entonces, ¿cómo se almacenan en la memoria?
z33k
2
@orety tienen que estar codificados de alguna manera internamente por exactamente esa razón, pero esto no es para usted exposiciones de código Python, como si no tuviera que preocuparse por cómo se almacenan los números de coma flotante.
lvc
1
@ChrisStryczynski ve los comentarios anteriores, seguro que de alguna manera están almacenados en la memoria , pero esa forma está explícitamente abstraída. De hecho, en estos días, puede cambiar durante la vida útil de un programa y ser diferente entre diferentes cadenas o incluso puede ser más de uno (algunas codificaciones se almacenan en caché), dependiendo de los caracteres que contengan, pero es el único momento en el que debe preocuparse. es decir, si está pirateando la implementación del tipo de cadena en sí.
lvc
390

Lo único que una computadora puede almacenar es bytes.

Para almacenar cualquier cosa en una computadora, primero debe codificarla , es decir, convertirla a bytes. Por ejemplo:

  • Si desea almacenar música, primero debe codificar utilizando MP3, WAV, etc.
  • Si desea almacenar una imagen, primero debe codificar utilizando PNG, JPEG, etc.
  • Si desea almacenar el texto, primero debe codificar utilizando ASCII, UTF-8, etc.

MP3, WAV, PNG, JPEG, ASCIIY UTF-8son ejemplos de codificaciones . Una codificación es un formato para representar audio, imágenes, texto, etc. en bytes.

En Python, una cadena de bytes es solo eso: una secuencia de bytes. No es legible para los humanos. Debajo del capó, todo debe convertirse a una cadena de bytes antes de que pueda almacenarse en una computadora.

Por otro lado, una cadena de caracteres, a menudo simplemente llamada "cadena", es una secuencia de caracteres. Es legible para los humanos. Una cadena de caracteres no puede almacenarse directamente en una computadora, primero debe codificarse (convertirse en una cadena de bytes). Existen múltiples codificaciones a través de las cuales una cadena de caracteres se puede convertir en una cadena de bytes, como ASCIIy UTF-8.

'I am a string'.encode('ASCII')

El código Python anterior codificará la cadena 'I am a string'usando la codificación ASCII. El resultado del código anterior será una cadena de bytes. Si lo imprime, Python lo representará como b'I am a string'. Sin embargo, recuerde que las cadenas de bytes no son legibles por humanos , es solo que Python las decodifica desde ASCIIque las imprime. En Python, una cadena de bytes está representada por a b, seguida de la ASCIIrepresentación de la cadena de bytes .

Una cadena de bytes se puede decodificar nuevamente en una cadena de caracteres, si conoce la codificación que se utilizó para codificarla.

b'I am a string'.decode('ASCII')

El código anterior devolverá la cadena original 'I am a string'.

La codificación y la decodificación son operaciones inversas. Todo debe estar codificado antes de poder escribirse en el disco, y debe descodificarse antes de que un humano pueda leerlo.

Zenadix
fuente
59
Zenadix merece algunas felicitaciones aquí. Después de algunos años funcionando en este entorno, esta es la primera explicación que hizo clic conmigo. Puedo tatuarlo en mi otro brazo (un brazo ya tiene "El mínimo absoluto que todo desarrollador de software debe saber absolutamente, positivamente sobre Unicode y los juegos de caracteres (¡Sin excusas!) Por Joel Spolsky"
neil.millikin
44
Absolutamente brillante. Lúcido y fácil de entender. Sin embargo, me gustaría mencionar que esta línea: "Si lo imprime, Python lo representará como b'I am a string '" es cierto para Python3 como para Python2 bytes y str son lo mismo.
SRC
55
¡Te estoy otorgando esta recompensa por ofrecer una explicación muy legible para los humanos para poner algo de claridad en este tema!
fedorqui 'SO deja de dañar'
3
Gran respuesta. Lo único que quizás podría agregarse es señalar más claramente que históricamente, los programadores y los lenguajes de programación han tendido a suponer explícita o implícitamente que una secuencia de bytes y una cadena ASCII son lo mismo . Python 3 decidió romper explícitamente esta suposición, en mi humilde opinión.
nekomatic
44
Enlace a la publicación de Joel mencionada por @ neil.millikin arriba: joelonsoftware.com/2003/10/08/…
Kshitij Saraogi
14

Nota: Elaboraré más mi respuesta para Python 3 ya que el final de la vida de Python 2 está muy cerca.

En Python 3

bytesconsiste en secuencias de valores sin signo de 8 bits, mientras que strconsiste en secuencias de puntos de código Unicode que representan caracteres textuales de lenguajes humanos.

>>> # bytes
>>> b = b'h\x65llo'
>>> type(b)
<class 'bytes'>
>>> list(b)
[104, 101, 108, 108, 111]
>>> print(b)
b'hello'
>>>
>>> # str
>>> s = 'nai\u0308ve'
>>> type(s)
<class 'str'>
>>> list(s)
['n', 'a', 'i', '̈', 'v', 'e']
>>> print(s)
naïve

A pesar de que bytesy strparece funcionar de la misma manera, sus casos no son compatibles entre sí, es decir, bytesy strlos casos no se pueden utilizar junto con operadores como >y +. Además, tenga en cuenta que la comparación bytesy las strinstancias de igualdad, es decir ==, el uso , siempre se evaluarán Falseincluso cuando contengan exactamente los mismos caracteres.

>>> # concatenation
>>> b'hi' + b'bye' # this is possible
b'hibye'
>>> 'hi' + 'bye' # this is also possible
'hibye'
>>> b'hi' + 'bye' # this will fail
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't concat str to bytes
>>> 'hi' + b'bye' # this will also fail
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can only concatenate str (not "bytes") to str
>>>
>>> # comparison
>>> b'red' > b'blue' # this is possible
True
>>> 'red'> 'blue' # this is also possible
True
>>> b'red' > 'blue' # you can't compare bytes with str
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: '>' not supported between instances of 'bytes' and 'str'
>>> 'red' > b'blue' # you can't compare str with bytes
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: '>' not supported between instances of 'str' and 'bytes'
>>> b'blue' == 'red' # equality between str and bytes always evaluates to False
False
>>> b'blue' == 'blue' # equality between str and bytes always evaluates to False
False

Otro problema cuando se trata bytesy strestá presente cuando se trabaja con archivos que se devuelven utilizando la openfunción incorporada. Por un lado, si desea leer o escribir datos binarios en / desde un archivo, siempre abra el archivo usando un modo binario como 'rb' o 'wb'. Por otro lado, si desea leer o escribir datos Unicode en / desde un archivo, tenga en cuenta la codificación predeterminada de su computadora, por lo que si es necesario, pase el encodingparámetro para evitar sorpresas.

En Python 2

strconsiste en secuencias de valores de 8 bits, mientras que unicodeconsiste en secuencias de caracteres Unicode. Una cosa a tener en cuenta es que str, y unicodese puede utilizar junto con los operadores si strsólo se compone de caracteres ASCI de 7 bits.

Puede ser útil usar funciones de ayuda para convertir entre stry unicodeen Python 2, y entre bytesy stren Python 3.

lmiguelvargasf
fuente
4

De lo que es Unicode :

Básicamente, las computadoras solo manejan números. Almacenan letras y otros caracteres asignando un número para cada uno.

......

Unicode proporciona un número único para cada personaje, sin importar la plataforma, el programa ni el idioma.

Entonces, cuando una computadora representa una cadena, encuentra los caracteres almacenados en la computadora a través de su número Unicode único y estas cifras se almacenan en la memoria. Pero no puede escribir directamente la cadena en el disco o transmitir la cadena en la red a través de su número Unicode único porque estas cifras son simplemente un número decimal simple. Debe codificar la cadena en una cadena de bytes, como UTF-8. UTF-8es una codificación de caracteres capaz de codificar todos los caracteres posibles y almacena caracteres como bytes (parece que este ). Entonces, la cadena codificada se puede usar en todas partes porque UTF-8es casi compatible en todas partes. Cuando abres un archivo de texto codificado enUTF-8desde otros sistemas, su computadora lo decodificará y mostrará caracteres en él a través de su número único Unicode. Cuando un navegador recibe datos de cadena codificados UTF-8desde la red, decodificará los datos en cadena (supondrá que el navegador está UTF-8codificando) y mostrará la cadena.

En python3, puede transformar cadenas y cadenas de bytes entre sí:

>>> print('中文'.encode('utf-8'))
b'\xe4\xb8\xad\xe6\x96\x87'
>>> print(b'\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8'))
中文 

En una palabra, la cadena es para mostrar a los humanos para leer en una computadora y la cadena de bytes es para almacenar en el disco y la transmisión de datos.

Sam Yang
fuente
1

Unicode es un formato acordado para la representación binaria de caracteres y varios tipos de formato (por ejemplo, minúsculas / mayúsculas, nueva línea, retorno de carro) y otras "cosas" (por ejemplo, emojis). Una computadora no es menos capaz de almacenar una representación unicode (una serie de bits), ya sea en la memoria o en un archivo, que es capaz de almacenar una representación ASCII (una serie diferente de bits), o cualquier otra representación (serie de bits) )

Para que la comunicación tenga lugar, las partes en la comunicación deben acordar qué representación se utilizará.

Debido a que Unicode busca representar todos los caracteres posibles (y otras "cosas") utilizados en la comunicación entre humanos y entre computadoras, requiere una mayor cantidad de bits para la representación de muchos caracteres (o cosas) que otros sistemas de representación que busca representar un conjunto más limitado de personajes / cosas. Para "simplificar" y quizás para acomodar el uso histórico, la representación unicode se convierte casi exclusivamente en algún otro sistema de representación (por ejemplo, ascii) con el propósito de almacenar caracteres en archivos.

No es el caso que unicode no se pueda usar para almacenar caracteres en archivos o transmitirlos a través de cualquier canal de comunicaciones, simplemente que no es así.

El término "cadena" no está definido con precisión. "Cadena", en su uso común, se refiere a un conjunto de caracteres / cosas. En una computadora, esos caracteres pueden almacenarse en cualquiera de las diferentes representaciones bit a bit. Una "cadena de bytes" es un conjunto de caracteres almacenados utilizando una representación que utiliza ocho bits (ocho bits se denominan bytes). Dado que, en estos días, las computadoras usan el sistema Unicode (caracteres representados por un número variable de bytes) para almacenar caracteres en la memoria y cadenas de bytes (caracteres representados por bytes individuales) para almacenar caracteres en archivos, se debe usar una conversión antes de los caracteres representados en la memoria se moverá al almacenamiento en archivos.

Gordon Shephard
fuente
0

Tengamos una cadena simple de un carácter 'š'y codifíquela en una secuencia de bytes:

>>> 'š'.encode('utf-8')
b'\xc5\xa1'

Para el propósito de este ejemplo, visualicemos la secuencia de bytes en su forma binaria:

>>> bin(int(b'\xc5\xa1'.hex(), 16))
'0b1100010110100001'

Ahora generalmente no es posible decodificar la información sin saber cómo se codificó. Solo si sabe que utf-8se utilizó la codificación de texto, puede seguir el algoritmo para decodificar utf-8 y adquirir la cadena original:

11000101 10100001
   ^^^^^   ^^^^^^
   00101   100001

Puede mostrar el número binario 101100001como una cadena:

>>> chr(int('101100001', 2))
'š'
Jeyekomon
fuente
0

Los lenguajes Python incluyen stry bytescomo estándar "Tipos incorporados". En otras palabras, son ambas clases. No creo que valga la pena intentar racionalizar por qué Python se ha implementado de esta manera.

Dicho esto, stry bytesson muy similares entre sí. Ambos comparten la mayoría de los mismos métodos. Los siguientes métodos son exclusivos de la strclase:

casefold
encode
format
format_map
isdecimal
isidentifier
isnumeric
isprintable

Los siguientes métodos son exclusivos de la bytesclase:

decode
fromhex
hex
cincuenta y dos tarjetas
fuente