Domanda

Sto usando questo codice per ottenere l'output standard da un programma esterno:

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

Il metodo communic () restituisce una matrice di byte:

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

Tuttavia, mi piacerebbe lavorare con l'output come una normale stringa Python. Per poterlo stampare in questo modo:

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

Ho pensato che fosse ciò che binascii.b2a_qp () è il metodo per, ma quando l'ho provato ho ottenuto di nuovo lo stesso array di byte:

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

Qualcuno sa come convertire il valore dei byte in stringa? Voglio dire, usando le "batterie" invece di farlo manualmente. E vorrei che andasse bene con Python 3.

È stato utile?

Soluzione

È necessario decodificare l'oggetto byte per produrre una stringa:

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

Altri suggerimenti

Penso che in questo modo sia facile:

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

Devi decodificare la stringa di byte e trasformarla in una stringa di caratteri (unicode).

Su Python 2

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

Su Python 3

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

Se non conosci la codifica, quindi per leggere l'input binario in stringa in modo compatibile con Python 3 e Python 2, usa l'antico MS-DOS cp437 codifica:

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

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

Poiché la codifica è sconosciuta, si aspettano che i simboli non inglesi vengano tradotti in caratteri di cp437 (i caratteri inglesi non vengono tradotti, poiché corrispondono nella maggior parte delle codifiche a byte singolo e UTF-8).

La decodifica dell'input binario arbitrario in UTF-8 non è sicura, perché potresti ottenere questo:

>>> 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 stesso vale per latin-1 , che era popolare (predefinito?) per Python 2. Vedi i punti mancanti in Layout codepage - è dove Python soffoca con il famigerato ordinale non nel range .

AGGIORNAMENTO 20150604 : si vocifera che Python 3 abbia una strategia di errore surrogateescape per codificare elementi in dati binari senza perdita di dati e arresti anomali, ma necessita di test di conversione [binario] - > [str] - > [binario] per convalidare sia le prestazioni che l'affidabilità.

AGGIORNAMENTO 20170116 : grazie al commento di Nearoo - esiste anche la possibilità di eliminare tutti i byte sconosciuti con il gestore degli errori backslashreplace . Funziona solo con Python 3, quindi anche con questa soluzione otterrai un output incoerente da diverse versioni di 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'))

Vedi https: //docs.python. org / 3 / howto / unicode.html # python-s-unicode-support per i dettagli.

AGGIORNAMENTO 20170119 : ho deciso di implementare la decodifica di escape della barra che funziona sia per Python 2 che per Python 3. Dovrebbe essere più lenta la soluzione cp437 , ma dovrebbe produrre < strong> risultati identici su ogni versione di 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'))

In Python 3 , la codifica predefinita è " utf-8 " , quindi puoi utilizzare direttamente:

b'hello'.decode()

che equivale a

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

D'altra parte, in Python 2 , codifica il valore predefinito è la codifica stringa predefinita. Pertanto, dovresti usare:

b'hello'.decode(encoding)

dove encoding è la codifica desiderata.

Nota: supporto per gli argomenti delle parole chiave è stato aggiunto in Python 2.7.

Penso che quello che vuoi davvero sia questo:

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

La risposta di Aaron era corretta, tranne per il fatto che devi sapere quale codifica usare. E credo che Windows usi 'windows-1252'. Importa solo se hai dei caratteri insoliti (non ascii) nei tuoi contenuti, ma poi farà la differenza.

A proposito, il fatto che sia IMPORTANTE è la ragione per cui Python si è trasferito utilizzando due tipi diversi di dati binari e di testo: non può convertirsi magicamente tra loro perché non conosce la codifica a meno che tu non lo dica ! L'unico modo per farti conoscere è leggere la documentazione di Windows (o leggerla qui).

Imposta universal_newlines su True, ad esempio

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

Mentre La risposta di @Aaron Maenpaa funziona, un utente richieste recenti :

  

Esiste un modo più semplice? 'fhand.read (). decode (" ASCII ")' [...] È così lungo!

Puoi usare:

command_stdout.decode()

decode () ha un argomento standard :

  

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

Per interpretare una sequenza di byte come un testo, devi conoscere il codifica dei caratteri corrispondenti:

unicode_text = bytestring.decode(character_encoding)

Esempio:

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

ls può produrre output che non può essere interpretato come testo. Nomi dei file su Unix può esserci qualsiasi sequenza di byte tranne la barra b '/' e zero b '\ 0' :

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

Il tentativo di decodificare tale byte byte utilizzando la codifica utf-8 genera UnicodeDecodeError .

Può essere peggio. La decodifica potrebbe non riuscire in silenzio e produrre mojibake se si utilizza una codifica incompatibile errata:

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

I dati sono danneggiati ma il programma rimane ignaro di un errore si è verificato.

In generale, quale codifica dei caratteri utilizzare non è incorporata nella sequenza di byte stessa. Devi comunicare queste informazioni fuori banda. Alcuni risultati sono più probabili di altri e quindi esiste un modulo chardet che può indovinare la codifica dei caratteri. Un singolo script Python può utilizzare più codifiche di caratteri in luoghi diversi.


L'output

ls può essere convertito in una stringa Python usando os.fsdecode () funzione che riesce anche per non decodificabile nomi di file (utilizza Gestore degli errori sys.getfilesystemencoding () e surrogateescape attivo Unix):

import os
import subprocess

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

Per ottenere i byte originali, è possibile utilizzare os.fsencode () .

Se passi il parametro universal_newlines = True , sottoprocesso usa locale.getpreferredencoding (False) per decodificare byte, ad esempio, può essere cp1252 su Windows.

Per decodificare il flusso di byte al volo, io.TextIOWrapper () potrebbe essere utilizzato: esempio .

Comandi diversi possono usare codifiche di caratteri differenti per loro output, ad esempio, il comando interno dir ( cmd ) può usare cp437. Per decodificare il suo output, potresti passare esplicitamente la codifica (Python 3.6+):

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

I nomi dei file possono differire da os.listdir () (che utilizza Windows API Unicode), ad es. '\ xb6' può essere sostituito con '\ x14' & # 8212; Python's il codec cp437 mappa b '\ x14' per controllare il carattere U + 0014 anziché U + 00B6 (& # 182;). Per supportare nomi di file con caratteri Unicode arbitrari, vedi Decodifica l'output Poweshell che probabilmente contiene caratteri unicode non ascii in una stringa Python

Dato che questa domanda si sta effettivamente ponendo sull'output di subprocess , hai un approccio più diretto disponibile poiché Popen accetta un parola chiave codifica (in 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 risposta generale per gli altri utenti è decodificare byte in testo:

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

Senza alcun argomento, sys.getdefaultencoding () verrà utilizzato. Se i tuoi dati non sono sys.getdefaultencoding () , devi specificare esplicitamente la codifica in decode call:

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

Se dovessi ottenere quanto segue provando decode () :

  

AttributeError: l'oggetto 'str' non ha attributi 'decodifica'

Puoi anche specificare il tipo di codifica direttamente in un cast:

>>> my_byte_str
b'Hello World'

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

Quando si lavora con dati da sistemi Windows (con \ r \ n terminazioni di riga), la mia risposta è

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

Perché? Prova questo con un Input.txt multilinea:

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

Tutte le terminazioni saranno raddoppiate (in \ r \ r \ n ), portando a righe vuote in più. Le funzioni di lettura del testo di Python normalmente normalizzano le terminazioni di riga in modo che le stringhe utilizzino solo \ n . Se ricevi dati binari da un sistema Windows, Python non ha la possibilità di farlo. Così,

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

replicherà il tuo file originale.

Ho creato una funzione per pulire un elenco

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

Per Python 3, questo è un approccio molto più sicuro e Pythonic per convertire da 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')

Output:

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

Se vuoi convertire qualsiasi byte, non solo una stringa convertita in byte:

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

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

Questo non è molto efficiente, tuttavia. Trasformerà un'immagine da 2 Mb in 9 Mb.

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

Per scrivere o leggere dati binari da / verso flussi standard, utilizzare il buffer binario sottostante. Ad esempio, per scrivere byte su stdout, usa sys.stdout.buffer.write (b'abc ') .

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top