Pregunta

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

¿Alguien sabe cómo volver a convertir el valor de los bytes en una cadena? Quiero decir, usando las " baterías " En lugar de hacerlo manualmente. Y me gustaría que estuviera bien con Python 3.

¿Fue útil?

Solución

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'

Otros consejos

Creo que esta manera es fácil:

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

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

En Python 2

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

En Python 3

encoding = 'utf-8'
str(b'hello', encoding)

Si no conoce la codificación, para leer la entrada binaria en la cadena de Python 3 y Python 2, use MS-DOS antiguo 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 se desconoce la codificación, 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).

La decodificación de entradas binarias arbitrarias en UTF-8 no es segura, ya que 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 fue popular (¿predeterminado?) para Python 2. Consulte los puntos que faltan en Diseño de la página de código - es donde Python se ahoga con el infame ordinal no dentro del rango .

ACTUALIZACIÓN 20150604 : hay rumores de que Python 3 tiene una estrategia de error surrogateescape para codificar cosas en datos binarios sin pérdida de datos ni bloqueos, pero necesita pruebas de conversión [binario] - > [str] - > [binario] 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. para más detalles. org / 3 / howto / unicode.html # python-s-unicode-support

ACTUALIZACIÓN 20170119 : decidí implementar una decodificación de barra diagonal que funciona tanto para Python 2 como para Python 3. Debería ser más lenta que la solución cp437 , pero debería producir < fuertes> 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 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 , codificación por defecto a la codificación de cadena predeterminada. Por lo tanto, debes usar:

b'hello'.decode(encoding)

donde codificación es la codificación que desea.

Nota: asistencia 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 necesitas saber QUÉ codificación usar. Y creo que Windows usa 'windows-1252'. Solo importará si tienes algunos caracteres inusuales (no ascii) en tu 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 codificación a menos que se lo digas. ! La única forma que USTED sabría es leer la documentación de Windows (o leerla aquí).

Establezca universal_newlines en True, es decir,

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

Si bien @Aaron Maenpaa responde simplemente funciona, un usuario preguntado 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 codificación de caracteres correspondiente:

unicode_text = bytestring.decode(character_encoding)

Ejemplo:

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

ls puede producir resultados que no pueden interpretarse como texto. Nombres de archivos en Unix puede haber cualquier secuencia de bytes excepto una barra inclinada b '/' y cero b '\ 0' :

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

Intentar descodificar dicha sopa de bytes con la codificación utf-8 genera UnicodeDecodeError .

Puede ser peor. La decodificación puede fallar en silencio y producir mojibake Si utiliza una codificación incompatible incorrecta:

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

Los datos están dañados, pero su programa sigue sin saber que hay un error ha ocurrido.

En general, la codificación de caracteres que se va a 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 lo tanto, existe un módulo chardet que puede adivinar la codificación de caracteres. Una sola secuencia de comandos de Python puede usar varias codificaciones de caracteres en diferentes lugares.


La salida de

ls se puede convertir a una cadena Python usando os.fsdecode () función que tiene éxito incluso para indecodible nombres de archivo (utiliza Controlador de errores sys.getfilesystemencoding () y 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 el parámetro universal_newlines = True , entonces subprocess utiliza locale.getpreferredencoding (False) para decodificar bytes, por ejemplo, puede ser cp1252 en Windows.

Para decodificar el flujo de bytes sobre la marcha, io.TextIOWrapper () podría utilizarse: ejemplo .

Los diferentes comandos pueden usar diferentes codificaciones de caracteres para sus por ejemplo, el comando interno dir ( cmd ) puede usar cp437. Para decodificar su salida, podría 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 Windows API de Unicode) por ejemplo, '\ xb6' se puede sustituir por '\ x14' —Python's cp437 codec mapea b '\ x14' para controlar el carácter U + 0014 en lugar de U + 00B6 (¶). Para admitir nombres de archivos con caracteres Unicode arbitrarios, consulte Descodifique la salida de poweshell que posiblemente contenga caracteres Unicode que no sean ASCII en una cadena de python

Dado que esta pregunta realmente se refiere a la salida de subprocess , tiene un enfoque más directo disponible ya que Popen acepta un codificación palabra clave (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 al texto:

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

Sin ningún argumento, sys.getdefaultencoding () . Si sus datos no son sys.getdefaultencoding () , debe especificar la codificación explícitamente en decode call:

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

Si obtienes lo siguiente al intentar decode () :

  

AttributeError: el objeto 'str' no tiene ningún atributo 'decodificar'

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

>>> my_byte_str
b'Hello World'

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

Al trabajar con datos de sistemas Windows (con \ r \ n líneas finales), 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 sus terminaciones de línea se duplicarán (a \ r \ r \ n ), lo que llevará a líneas extra vacías. 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. 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á tu archivo original.

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

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

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

Si desea convertir cualquier byte, no solo la cadena convertida en 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 eficiente. Convierte una imagen de 2 mb en 9 mb.

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 la salida estándar, use sys.stdout.buffer.write (b'abc ') .

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top