Convertir bytes en una cadena

Estoy usando este código para obtener una salida estándar de un programa externo:

>>> from subprocess import *
>>> command_stdout = Popen(['ls', '-l'], stdout=PIPE).communicate()[0]

El método comunica () devuelve una matriz de bytes:

>>> command_stdout
b'total 0\n-rw-rw-r-- 1 thomas thomas 0 Mar  3 07:03 file1\n-rw-rw-r-- 1 thomas thomas 0 Mar  3 07:03 file2\n'

Sin embargo, me gustaría trabajar con la salida como una cadena Python normal. Para poder imprimirlo así:

>>> print(command_stdout)
-rw-rw-r-- 1 thomas thomas 0 Mar  3 07:03 file1
-rw-rw-r-- 1 thomas thomas 0 Mar  3 07:03 file2

Pensé que eso es lo que binascii.b2a_qp () El método es para, pero cuando lo probé, obtuve la misma matriz de bytes nuevamente:

>>> binascii.b2a_qp(command_stdout)
b'total 0\n-rw-rw-r-- 1 thomas thomas 0 Mar  3 07:03 file1\n-rw-rw-r-- 1 thomas thomas 0 Mar  3 07:03 file2\n'

¿Cómo convierto el valor de bytes a una cadena? Quiero decir, usando las "baterías" en lugar de hacerlo manualmente. Y me gustaría que estuviera bien con Python 3.

preguntado el 03 de marzo de 09 a las 10:03

por qué no str(text_bytes) ¿trabaja? Esto me parece extraño. -

@CharlieParker Porque str(text_bytes) no se puede especificar la codificación. Dependiendo de lo que haya en text_bytes, text_bytes.decode('cp1250) `podría resultar en una cadena muy diferente a text_bytes.decode('utf-8'). -

so str La función ya no se convierte en una cadena real. Uno TIENE que decir una codificación explícitamente por alguna razón, soy demasiado vago para leer el por qué. Simplemente conviértalo a utf-8 y vea si su código funciona. p.ej var = var.decode('utf-8') -

@CraigAnderson: unicode_text = str(bytestring, character_encoding) funciona como se esperaba en Python 3. Aunque unicode_text = bytestring.decode(character_encoding) es más preferible evitar confusiones con solo str(bytes_obj) que produce una representación de texto para bytes_obj en lugar de decodificarlo en texto: str(b'\xb6', 'cp1252') == b'\xb6'.decode('cp1252') == '¶' y str(b'\xb6') == "b'\\xb6'" == repr(b'\xb6') != '¶' -

19 Respuestas

Necesita decodificar el objeto bytes para producir una cadena:

>>> b"abcde"
b'abcde'

# utf-8 is used here because it is a very common encoding, but you
# need to use the encoding your data is actually in.
>>> b"abcde".decode("utf-8") 
'abcde'

Respondido 24 Jul 15, 19:07

Usar "windows-1252" tampoco es confiable (por ejemplo, para otras versiones de idioma de Windows), ¿no sería mejor usar sys.stdout.encoding? - Niko

Tal vez esto ayude a alguien más: a veces usa una matriz de bytes para la comunicación ex TCP. Si desea convertir una matriz de bytes en una cadena que corta los caracteres '\ x00' finales, la siguiente respuesta no es suficiente. Utilice b'example \ x00 \ x00'.decode ('utf-8'). Strip ('\ x00') luego. - wookie88

He llenado un error acerca de documentarlo en bugs.python.org/issue17860 - siéntete libre de proponer un parche. Si es difícil contribuir, los comentarios sobre cómo mejorar son bienvenidos. - anatoly techtonik

En Python 2.7.6 no maneja b"\x80\x02\x03".decode("utf-8") -> UnicodeDecodeError: 'utf8' codec can't decode byte 0x80 in position 0: invalid start byte. - martillo

Si el contenido son valores binarios aleatorios, el utf-8 es probable que la conversión falle. En su lugar, vea la respuesta de @techtonik (a continuación) stackoverflow.com/a/27527728/198536 - Wallyk

Debe decodificar la cadena de bytes y convertirla en una cadena de caracteres (Unicode).

En Python 2

encoding = 'utf-8'
'hello'.decode(encoding)

or

unicode('hello', encoding)

En Python 3

encoding = 'utf-8'
b'hello'.decode(encoding)

or

str(b'hello', encoding)

Respondido el 28 de Septiembre de 19 a las 11:09

En Python 3, ¿qué pasa si la cadena está en una variable? - Ala M.

@AlaaM .: lo mismo. Si usted tiene variable = b'hello', entonces unicode_text = variable.decode(character_encoding) - jfs

para mi, variable = variable.decode() Automágicamente lo conseguí en un formato de cadena que quería. - Alex Salón

Creo que de esta manera es fácil:

>>> bytes_data = [112, 52, 52]
>>> "".join(map(chr, bytes_data))
'p44'

contestado el 14 de mayo de 20 a las 19:05

Gracias, su método funcionó para mí cuando ningún otro lo hizo. Tenía una matriz de bytes no codificada que necesitaba convertir en una cadena. Estaba tratando de encontrar una manera de volver a codificarlo para poder decodificarlo en una cadena. ¡Este método funciona perfectamente! - leetNocturna

@leetNightshade: sin embargo, es terriblemente ineficiente. Si tiene una matriz de bytes, solo necesita decodificar. - Martijn Pieters ♦

@Martijn Pieters Acabo de hacer un punto de referencia simple con estas otras respuestas, ejecutando múltiples 10,000 ejecuciones stackoverflow.com/a/3646405/353094 Y la solución anterior fue en realidad mucho más rápida cada vez. Para 10,000 ejecuciones en Python 2.7.7, toma 8 ms, en comparación con las otras a 12 ms y 18 ms. De acuerdo, podría haber alguna variación dependiendo de la entrada, la versión de Python, etc. No me parece demasiado lento. - leetNocturna

@Martijn Pieters Sí. Entonces, con ese punto, esta no es la mejor respuesta para el cuerpo de la pregunta que se hizo. Y el título es engañoso, ¿no? Quiere convertir una cadena de bytes en una cadena normal, no una matriz de bytes en una cadena. Esta respuesta funciona bien para el título de la pregunta que se hizo. - leetNocturna

Para Python 3, esto debería ser equivalente a bytes([112, 52, 52]) - por cierto, bytes es un mal nombre para una variable local exactamente porque es una p3 incorporada - Sr_y_Sra_D

Si no conoce la codificación, entonces para leer la entrada binaria en una cadena en forma compatible con Python 3 y Python 2, use el antiguo MS-DOS CP437 codificación:

PY3K = sys.version_info >= (3, 0)

lines = []
for line in stream:
    if not PY3K:
        lines.append(line)
    else:
        lines.append(line.decode('cp437'))

Debido a que la codificación es desconocida, espere que los símbolos que no estén en inglés se traduzcan a caracteres de cp437 (Los caracteres en inglés no se traducen porque coinciden en la mayoría de las codificaciones de un solo byte y UTF-8).

Decodificar una entrada binaria arbitraria a UTF-8 no es seguro, porque puede obtener esto:

>>> b'\x00\x01\xffsd'.decode('utf-8')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 2: invalid
start byte

Lo mismo se aplica a latin-1, que era popular (¿el predeterminado?) para Python 2. Vea los puntos faltantes en Diseño de página de códigos - es donde Python se ahoga con infames ordinal not in range.

ACTUALIZACIÓN 20150604: Hay rumores de que Python 3 tiene la surrogateescape estrategia de error para codificar cosas en datos binarios sin pérdida de datos y fallas, pero necesita pruebas de conversión, [binary] -> [str] -> [binary], para validar tanto el rendimiento como la fiabilidad.

ACTUALIZACIÓN 20170116: Gracias al comentario de Nearoo, también existe la posibilidad de eliminar todos los bytes desconocidos con backslashreplace manejador de errores. Eso funciona solo para Python 3, por lo que incluso con esta solución, seguirá obteniendo resultados inconsistentes de diferentes versiones de Python:

PY3K = sys.version_info >= (3, 0)

lines = []
for line in stream:
    if not PY3K:
        lines.append(line)
    else:
        lines.append(line.decode('utf-8', 'backslashreplace'))

Vea Soporte Unicode de Python para más detalles.

ACTUALIZACIÓN 20170119: Decidí implementar la decodificación de escape de barra que funciona tanto para Python 2 como para Python 3. Debería ser más lento que el cp437 solución, pero debería producir resultados idénticos en cada versión de Python.

# --- preparation

import codecs

def slashescape(err):
    """ codecs error handler. err is UnicodeDecode instance. return
    a tuple with a replacement for the unencodable part of the input
    and a position where encoding should continue"""
    #print err, dir(err), err.start, err.end, err.object[:err.start]
    thebyte = err.object[err.start:err.end]
    repl = u'\\x'+hex(ord(thebyte))[2:]
    return (repl, err.end)

codecs.register_error('slashescape', slashescape)

# --- processing

stream = [b'\x80abc']

lines = []
for line in stream:
    lines.append(line.decode('utf-8', 'slashescape'))

Respondido el 28 de Septiembre de 19 a las 11:09

Realmente siento que Python debería proporcionar un mecanismo para reemplazar los símbolos que faltan y continuar. - anatoly techtonik

@techtonik: esto no funcionará en una matriz como lo hizo en python2. - user2284570

@ user2284570 ¿te refieres a la lista? ¿Y por qué debería funcionar en matrices? Especialmente matrices de flotadores. anatoly techtonik

También puede ignorar los errores Unicode con b'\x00\x01\xffsd'.decode('utf-8', 'ignore') en python 3. - Antonis Kalou

@anatolytechtonik Existe la posibilidad de dejar la secuencia de escape en la cadena y seguir adelante: b'\x80abc'.decode("utf-8", "backslashreplace") resultará en '\\x80abc'. Esta información fue tomada de la página de documentación unicode que parece haberse actualizado desde la redacción de esta respuesta. - Nearoo

En Python 3, la codificación predeterminada es "utf-8", para que pueda usar directamente:

b'hello'.decode()

que es equivalente a

b'hello'.decode(encoding="utf-8")

Por otra parte, en Python 2, la codificación tiene como valor predeterminado la codificación de cadena predeterminada. Por lo tanto, debes usar:

b'hello'.decode(encoding)

dónde encoding es la codificación que desea.

Nota: Se agregó soporte para argumentos de palabras clave en Python 2.7.

Respondido el 28 de Septiembre de 19 a las 11:09

Creo que realmente quieres esto:

>>> from subprocess import *
>>> command_stdout = Popen(['ls', '-l'], stdout=PIPE).communicate()[0]
>>> command_text = command_stdout.decode(encoding='windows-1252')

La respuesta de Aaron fue correcta, excepto que necesitas saber que codificación para usar. Y creo que Windows usa 'windows-1252'. Solo importará si tiene algunos caracteres inusuales (no ASCII) en su contenido, pero entonces marcará la diferencia.

Por cierto, el hecho de que La cuestión es la razón por la que Python pasó a usar dos tipos diferentes para datos binarios y de texto: ¡no puede convertir mágicamente entre ellos, porque no conoce la codificación a menos que usted lo diga! La única forma en que USTED lo sabría es leer la documentación de Windows (o leerla aquí).

Respondido el 28 de Septiembre de 19 a las 11:09

open() función para flujos de texto o Popen() si lo pasas universal_newlines=True decide mágicamente la codificación de caracteres por tilocale.getpreferredencoding(False) en Python 3.3+). - jfs

'latin-1' es una codificación literal con todos los puntos de código establecidos, por lo que puede usar eso para leer de manera efectiva una cadena de bytes en cualquier tipo de cadena que admita su Python (por lo tanto, textualmente en Python 2, en Unicode para Python 3). - triples

@tripleee: 'latin-1' es una buena forma de conseguir mojibake. También hay una sustitución mágica en Windows: es sorprendentemente difícil canalizar datos de un proceso a otro sin modificar, por ejemplo, dir: \xb6 -> \x14 (el ejemplo al final de mi respuesta) - jfs

Establezca universal_newlines en True, es decir

command_stdout = Popen(['ls', '-l'], stdout=PIPE, universal_newlines=True).communicate()[0]

Respondido el 21 de enero de 14 a las 15:01

He estado usando este método y funciona. Aunque, solo se trata de adivinar la codificación en función de las preferencias del usuario en su sistema, por lo que no es tan sólido como otras opciones. Esto es lo que está haciendo, haciendo referencia a docs.python.org/3.4/library/subprocess.html: "Si universal_newlines es True, [stdin, stdout y stderr] se abrirán como flujos de texto en modo universal de nuevas líneas usando la codificación devuelta por la configuración regional .getpreferredencoding (falso) ". - twasbrillig

En 3.7 puedes (y debes) hacer text=True en lugar de universal_newlines=True. - Boris

Para interpretar una secuencia de bytes como texto, debe conocer la codificación de caracteres correspondiente:

unicode_text = bytestring.decode(character_encoding)

Ejemplo:

>>> b'\xc2\xb5'.decode('utf-8')
'µ'

ls El comando puede producir una salida que no se puede interpretar como texto. Los nombres de archivo en Unix pueden ser cualquier secuencia de bytes excepto barra b'/' y cero b'\0':

>>> open(bytes(range(0x100)).translate(None, b'\0/'), 'w').close()

Intentar decodificar tal sopa de bytes usando la codificación utf-8 aumenta UnicodeDecodeError.

Puede ser peor. La decodificación puede fallar silenciosamente y producir mojibake si usa una codificación incompatible incorrecta:

>>> '—'.encode('utf-8').decode('cp1252')
'—'

Los datos están dañados, pero su programa no se da cuenta de que se ha producido un error.

En general, qué codificación de caracteres usar no está incrustada en la propia secuencia de bytes. Tienes que comunicar esta información fuera de banda. Algunos resultados son más probables que otros y, por lo tanto, chardet existe un módulo que puede adivinar la codificación de caracteres. Una sola secuencia de comandos de Python puede usar múltiples codificaciones de caracteres en diferentes lugares.


ls la salida se puede convertir a una cadena de Python usando os.fsdecode() función que tiene éxito incluso para nombres de archivo no codificables (usa sys.getfilesystemencoding() y surrogateescape controlador de errores en Unix):

import os
import subprocess

output = os.fsdecode(subprocess.check_output('ls'))

Para obtener los bytes originales, puede usar os.fsencode().

Si pasa universal_newlines=True parámetro entonces subprocess usos locale.getpreferredencoding(False) para decodificar bytes, por ejemplo, puede ser cp1252 en Windows.

Para decodificar el flujo de bytes sobre la marcha, io.TextIOWrapper() puede ser usado: ejemplo.

Los diferentes comandos pueden usar diferentes codificaciones de caracteres para su salida, por ejemplo, dir comando internocmd) puede utilizar cp437. Para decodificar su salida, puede pasar la codificación explícitamente (Python 3.6+):

output = subprocess.check_output('dir', shell=True, encoding='cp437')

Los nombres de archivo pueden diferir de os.listdir() (que usa la API de Windows Unicode), por ejemplo, '\xb6' puede ser sustituido por '\x14'—Mapas del códec cp437 de Python b'\x14' para controlar el carácter U + 0014 en lugar de U + 00B6 (¶). Para admitir nombres de archivo con caracteres Unicode arbitrarios, consulte Decodificar la salida de PowerShell que posiblemente contenga caracteres Unicode no ASCII en una cadena de Python

Respondido 04 Oct 19, 21:10

Dado que esta pregunta en realidad es sobre subprocess salida, tiene enfoques más directos disponibles. El más moderno estaría usando subprocess.check_output y pasando text=True (Python 3.7+) para decodificar automáticamente stdout usando la codificación predeterminada del sistema:

text = subprocess.check_output(["ls", "-l"], text=True)

Para Python 3.6, Popen acepta un codificación palabra clave:

>>> from subprocess import Popen, PIPE
>>> text = Popen(['ls', '-l'], stdout=PIPE, encoding='utf-8').communicate()[0]
>>> type(text)
str
>>> print(text)
total 0
-rw-r--r-- 1 wim badger 0 May 31 12:45 some_file.txt

La respuesta general a la pregunta del título, si no está tratando con la salida del subproceso, es descodificar bytes al texto:

>>> b'abcde'.decode()
'abcde'

Sin argumento sys.getdefaultencoding() se utilizará. Si sus datos no son sys.getdefaultencoding(), debe especificar la codificación explícitamente en el decode llamada:

>>> b'caf\xe9'.decode('cp1250')
'café'

Respondido el 04 de Septiembre de 20 a las 19:09

Descodificación ls salida usando utf-8 la codificación puede fallar (ver ejemplo en mi respuesta de 2016). - jfs

@Boris: si encoding se da el parámetro, entonces el text el parámetro se ignora. - jfs

Aunque la La respuesta de @Aaron Maenpaa simplemente funciona, un usuario pedido recientemente:

¿Hay alguna forma más sencilla? 'fhand.read (). decode ("ASCII")' [...] ¡Es tan largo!

Puedes usar:

command_stdout.decode()

decode() tiene un argumento estándar:

codecs.decode(obj, encoding='utf-8', errors='strict')

Respondido el 10 de junio de 19 a las 17:06

.decode() que los usos 'utf-8' puede fallar (la salida del comando puede usar una codificación de caracteres diferente o incluso devolver una secuencia de bytes no codificable). Aunque si la entrada es ascii (un subconjunto de utf-8) entonces .decode() obras. - jfs

Si debe obtener lo siguiente al intentar decode():

AttributeError: el objeto 'str' no tiene atributo 'decode'

También puede especificar el tipo de codificación directamente en un reparto:

>>> my_byte_str
b'Hello World'

>>> str(my_byte_str, 'utf-8')
'Hello World'

Respondido el 10 de junio de 19 a las 17:06

Hice una función para limpiar una lista

def cleanLists(self, lista):
    lista = [x.strip() for x in lista]
    lista = [x.replace('\n', '') for x in lista]
    lista = [x.replace('\b', '') for x in lista]
    lista = [x.encode('utf8') for x in lista]
    lista = [x.decode('utf8') for x in lista]

    return lista

respondido 09 mar '17, 10:03

De hecho, puede encadenar todos los .strip, .replace, .encode, etc llama a la comprensión de una lista y solo itera sobre la lista una vez en lugar de iterar sobre ella cinco veces. - taylor edmiston

@TaylorEdmiston Tal vez ahorre en la asignación, pero el número de operaciones seguirá siendo el mismo. - JulienD

Al trabajar con datos de sistemas Windows (con \r\n finales de línea), mi respuesta es

String = Bytes.decode("utf-8").replace("\r\n", "\n")

¿Por qué? Intente esto con un Input.txt multilínea:

Bytes = open("Input.txt", "rb").read()
String = Bytes.decode("utf-8")
open("Output.txt", "w").write(String)

Todas las terminaciones de sus líneas se duplicarán (para \r\r\n), lo que genera más líneas vacías. Las funciones de lectura de texto de Python normalmente normalizan las terminaciones de línea para que las cadenas solo utilicen \n. Si recibe datos binarios de un sistema Windows, Python no tiene la oportunidad de hacerlo. Por lo tanto,

Bytes = open("Input.txt", "rb").read()
String = Bytes.decode("utf-8").replace("\r\n", "\n")
open("Output.txt", "w").write(String)

replicará su archivo original.

respondido 16 mar '18, 13:03

estaba buscando .replace("\r\n", "\n") Además tanto tiempo. Esta es la respuesta si desea renderizar HTML correctamente. - mhlavacka

Para Python 3, este es un método mucho más seguro y Pitónico enfoque para convertir de byte a string:

def byte_to_str(bytes_or_str):
    if isinstance(bytes_or_str, bytes): # Check if it's in bytes
        print(bytes_or_str.decode('utf-8'))
    else:
        print("Object not of byte type")

byte_to_str(b'total 0\n-rw-rw-r-- 1 thomas thomas 0 Mar  3 07:03 file1\n-rw-rw-r-- 1 thomas thomas 0 Mar  3 07:03 file2\n')

Salida:

total 0
-rw-rw-r-- 1 thomas thomas 0 Mar  3 07:03 file1
-rw-rw-r-- 1 thomas thomas 0 Mar  3 07:03 file2

Respondido el 28 de Septiembre de 19 a las 12:09

1) Como dijo @bodangly, la verificación de tipos no es pitónica en absoluto. 2) La función que escribiste se llama "byte_to_str"lo que implica que devolverá una cadena, pero solo imprime el valor convertido, y imprime un mensaje de error si falla (pero no genera una excepción). Este enfoque tampoco es pitónico y ofusca la bytes.decode solución que proporcionaste. - CosmicFluke

Para tu soluciones caso de "ejecutar un comando de shell y obtener su salida como texto en lugar de bytes", en Python 3.7, debe usar subprocess.run y pasar text=True (tanto como capture_output=True para capturar la salida)

command_result = subprocess.run(["ls", "-l"], capture_output=True, text=True)
command_result.stdout  # is a `str` containing your program's stdout

text solía ser llamado universal_newlines, y fue cambiado (bueno, con alias) en Python 3.7. Si desea admitir versiones de Python anteriores a la 3.7, pase universal_newlines=True en lugar de text=True

Respondido 07 ago 19, 20:08

Desde sys: parámetros y funciones específicos del sistema:

Para escribir o leer datos binarios desde / hacia los flujos estándar, use el búfer binario subyacente. Por ejemplo, para escribir bytes en stdout, use sys.stdout.buffer.write(b'abc').

Respondido el 28 de Septiembre de 19 a las 11:09

La tubería al subproceso es ya haya utilizado un búfer binario. Su respuesta no aborda cómo obtener un valor de cadena del resultado bytes valor. - Martijn Pieters ♦

def toString(string):    
    try:
        return v.decode("utf-8")
    except ValueError:
        return string

b = b'97.080.500'
s = '97.080.500'
print(toString(b))
print(toString(s))

Respondido el 03 de junio de 18 a las 23:06

Si bien este código puede responder a la pregunta, proporciona información adicional contexto con respecto a cómo y/o el porqué resuelve el problema mejoraría el valor a largo plazo de la respuesta. Recuerde que está respondiendo la pregunta para los lectores en el futuro, ¡no solo la persona que la pregunta ahora! Por favor editar su respuesta para agregar una explicación y dar una indicación de las limitaciones y suposiciones que se aplican. Tampoco está de más mencionar por qué esta respuesta es más apropiada que otras. - Demonio

Una explicación estaría en orden. - Pedro Mortensen

Si desea convertir bytes, no solo cadenas convertidas a bytes:

with open("bytesfile", "rb") as infile:
    str = base64.b85encode(imageFile.read())

with open("bytesfile", "rb") as infile:
    str2 = json.dumps(list(infile.read()))

Sin embargo, esto no es muy eficaz. Convertirá una imagen de 2 MB en 9 MB.

Respondido el 28 de Septiembre de 19 a las 12:09

prueba este

bytes.fromhex('c3a9').decode('utf-8') 

Respondido el 19 de enero de 20 a las 08:01

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.