Convertir bytes a una cadena?

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

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

El método comunicar () 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 de Python normal. Para que yo pudiera 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 para eso es el método binascii.b2a_qp () , 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' 

¿Alguien sabe cómo convertir el valor de los bytes de nuevo a la cadena? Quiero decir, usar las “baterías” en lugar de hacerlo manualmente. Y me gustaría que estuviera bien con Python 3.

Necesitas 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' 

Creo que esta manera es fácil:

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

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

 b'hello'.decode(encoding) 

o en Python 3

 str(b'hello', encoding) 

Si no conoce la encoding, entonces para leer la entrada binaria en la cadena de Python 3 y Python 2, use la antigua encoding cp437 de MS-DOS:

 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 encoding 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 byte único y UTF-8).

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

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

Lo mismo se aplica a latin-1 , que fue popular (¿predeterminado?) Para Python 2. Consulte los puntos que faltan en el diseño de la página de códigos : es donde Python se ahoga con ordinal not in range infames que ordinal not in range .

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

ACTUALIZACIÓN 20170116 : Gracias al comentario de Nearoo, también existe la posibilidad de eliminar de forma brusca todos los bytes desconocidos con el controlador de errores backslashreplace . Eso solo funciona para Python 3, por lo que incluso con esta solución, aún obtendrás 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')) 

Consulte https://docs.python.org/3/howto/unicode.html#python-s-unicode-support para obtener más información.

ACTUALIZACIÓN 20170119 : Decidí implementar una deencoding de barra diagonal que funcione tanto para Python 2 como para Python 3. Debería ser más lenta que la solución cp437 , 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')) 

En Python 3 , la encoding predeterminada es "utf-8" , por lo que puede usar directamente:

 b'hello'.decode() 

que es equivalente a

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

Por otro lado, en Python 2 , la encoding predeterminada es la encoding de cadena predeterminada. Por lo tanto, debe utilizar:

 b'hello'.decode(encoding) 

donde la encoding es la encoding que desea.

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

Creo que lo que realmente quieres es 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 necesita saber QUÉ encoding usar. Y creo que Windows usa ‘windows-1252’. Solo importará si tiene algunos caracteres inusuales (no ascii) en su contenido, pero eso marcará la diferencia.

Por cierto, el hecho de que SÍ sea importante es la razón por la que Python se movió para usar dos tipos diferentes para datos binarios y de texto: ¡no se puede convertir mágicamente entre ellos porque no conoce la encoding a menos que se lo digas! La única forma que USTED sabría es leer la documentación de Windows (o leerla aquí).

Establecer universal_newlines en True, es decir

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

Si bien la respuesta de @Aaron Maenpaa simplemente funciona, un usuario preguntó recientemente

¿Hay alguna manera más simple? ‘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')

Para interpretar una secuencia de bytes como un texto, debe conocer la encoding de caracteres correspondiente:

 unicode_text = bytestring.decode(character_encoding) 

Ejemplo:

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

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

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

Intentar descodificar dicha sopa de bytes con la encoding utf-8 genera UnicodeDecodeError .

Puede ser peor La deencoding puede fallar de forma silenciosa y producir un mojibake si utiliza una encoding incompatible incorrecta:

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

Los datos están dañados, pero su progtwig sigue sin saber que se ha producido un error.

En general, la encoding de caracteres que se debe usar no está incorporada en la secuencia de bytes en sí. Tienes que comunicar esta información fuera de banda. Algunos resultados son más probables que otros y, por chardet tanto, existe un módulo chardet que puede adivinar la encoding de caracteres. Un solo script de Python puede usar múltiples codificaciones de caracteres en diferentes lugares.


ls salida de ls se puede convertir en una cadena Python usando la función os.fsdecode() que tiene éxito incluso para nombres de archivo no sys.getfilesystemencoding() usa sys.getfilesystemencoding() y el controlador de errores surrogateescape 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 universal_newlines=True , el subprocess usa locale.getpreferredencoding(False) para decodificar bytes, por ejemplo, puede ser cp1252 en Windows.

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

Diferentes comandos pueden usar diferentes codificaciones de caracteres para su salida, por ejemplo, dir comando interno ( cmd ) puede usar cp437. Para decodificar su salida, podría pasar la encoding 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 Unicode de Windows), por ejemplo, '\xb6' puede sustituirse por '\x14' : el códec cp437 de Python asigna b'\x14' para controlar el carácter U + 0014 en lugar de U + 00B6 (¶). Para admitir nombres de archivos con caracteres Unicode arbitrarios, vea Descodificar la salida de poweshell que posiblemente contenga caracteres Unicode que no sean ascii en una cadena de python

Ya que esta pregunta es realmente acerca de la salida de subprocess , tiene un enfoque más directo disponible ya que Popen acepta una palabra clave de encoding (en Python 3.6+):

 >>> 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 para otros usuarios es decodificar bytes a texto:

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

Sin ningún argumento, se sys.getdefaultencoding() . Si sus datos no son sys.getdefaultencoding() , debe especificar la encoding explícitamente en la llamada de decode :

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

Si obtienes lo siguiente intentando decode() :

AttributeError: 'str' object has no attribute 'decode'

También puede especificar el tipo de encoding directamente en una conversión:

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

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 

Cuando trabajo 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) 

Todos sus finales de línea se duplicarán (a \r\r\n ), lo que dará lugar a líneas vacías adicionales. Las funciones de lectura de texto de Python normalmente normalizan los finales de línea para que las cadenas solo usen \n . Si recibe datos binarios de un sistema Windows, Python no tiene la oportunidad de hacerlo. Así,

 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.

Para Python 3, este es un enfoque mucho más seguro y Pythonic para convertir de byte a string :

 def byte_to_str(bytes_or_str): if isinstance(bytes_or_str, bytes): #check if its 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 
 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)) 

De http://docs.python.org/3/library/sys.html ,

Para escribir o leer datos binarios de / a las secuencias estándar, use el búfer binario subyacente. Por ejemplo, para escribir bytes en stdout, use sys.stdout.buffer.write (b’abc ‘).