Converti byte in una stringa


2310

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 per questo il metodo binascii.b2a_qp () , 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'

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


47
perché non str(text_bytes)funziona? Questo mi sembra strano.
Charlie Parker,

13
@CharlieParker Perché str(text_bytes)non è possibile specificare la codifica. A seconda di cosa c'è in text_bytes, text_bytes.decode('cp1250) `potrebbe risultare in una stringa molto diversa da text_bytes.decode('utf-8').
Craig Anderson,

6
quindi la strfunzione non si converte più in una stringa reale. Uno DEVE dire esplicitamente una codifica per qualche motivo che sono pigro a leggere perché. Basta convertirlo in utf-8e vedere se il codice ur funziona. ad es.var = var.decode('utf-8')
Charlie Parker,

1
@CraigAnderson: unicode_text = str(bytestring, character_encoding)funziona come previsto su Python 3. Anche se unicode_text = bytestring.decode(character_encoding)è preferibile evitare confusione con str(bytes_obj)ciò che produce una rappresentazione testuale bytes_objanziché decodificarla in testo: str(b'\xb6', 'cp1252') == b'\xb6'.decode('cp1252') == '¶'estr(b'\xb6') == "b'\\xb6'" == repr(b'\xb6') != '¶'
jfs

Risposte:


3677

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

58
Anche l'utilizzo "windows-1252"non è affidabile (ad es. Per versioni in altre lingue di Windows), non sarebbe meglio usarlo sys.stdout.encoding?
nikow,

12
Forse questo aiuterà qualcuno ulteriormente: a volte usi array di byte per le comunicazioni ex TCP. Se si desidera convertire l'array di byte in stringa tagliando i caratteri finali "\ x00", la seguente risposta non è sufficiente. Usa b'example \ x00 \ x00'.decode ('utf-8'). Strip ('\ x00') quindi.
Wookie88,

2
Ho riempito un bug per documentarlo su bugs.python.org/issue17860 - sentiti libero di proporre una patch. Se è difficile contribuire, commenta come migliorarlo.
anatoly techtonik,

44
In Python 2.7.6 non gestisce b"\x80\x02\x03".decode("utf-8")-> UnicodeDecodeError: 'utf8' codec can't decode byte 0x80 in position 0: invalid start byte.
martineau,

9
Se il contenuto è valori binari casuali, utf-8è probabile che la conversione non riesca. Invece vedere @techtonik risposta (sotto) stackoverflow.com/a/27527728/198536
wallyk

215

È necessario decodificare la stringa di byte e trasformarla in una stringa di caratteri (Unicode).

Su Python 2

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

o

unicode('hello', encoding)

Su Python 3

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

o

str(b'hello', encoding)

2
Su Python 3, cosa succede se la stringa si trova in una variabile?
Alaa M.

1
@AlaaM .: lo stesso. Se sì variable = b'hello', alloraunicode_text = variable.decode(character_encoding)
jfs il

182

Penso che questo sia facile:

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

6
Grazie, il tuo metodo ha funzionato per me quando nessun altro ha funzionato. Avevo un array di byte non codificato che avevo bisogno di trasformare in una stringa. Stavo cercando di trovare un modo per ricodificarlo in modo da poterlo decodificare in una stringa. Questo metodo funziona perfettamente!
leetNightshade,

5
@leetNightshade: eppure è terribilmente inefficiente. Se hai un array di byte devi solo decodificare.
Martijn Pieters

12
@Martijn Pieters Ho appena fatto un semplice benchmark con queste altre risposte, eseguendo più di 10.000 corse stackoverflow.com/a/3646405/353094 E la soluzione di cui sopra è stata in realtà molto più veloce ogni volta. Per 10.000 esecuzioni in Python 2.7.7 sono necessari 8ms, rispetto agli altri a 12ms e 18ms. Concesso, potrebbero esserci alcune variazioni a seconda dell'input, della versione di Python, ecc. Non mi sembra troppo lento.
leetNightshade

5
@Martijn Pieters Sì. Quindi, a quel punto, questa non è la risposta migliore per il corpo della domanda che è stata posta. E il titolo è fuorviante, no? Vuole convertire una stringa di byte in una stringa normale, non una matrice di byte in una stringa. Questa risposta funziona bene per il titolo della domanda che è stata posta.
leetNightshade

5
Per python 3 questo dovrebbe essere equivalente a bytes([112, 52, 52])- btw byte è un brutto nome per una variabile locale proprio perché è un built-in p3
Mr_and_Mrs_D

92

Se non si conosce la codifica, quindi per leggere l'input binario in stringa in modo compatibile con Python 3 e Python 2, utilizzare la codifica CP437 MS-DOS antica :

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 cp437vengano tradotti in caratteri di (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 (il valore predefinito?) Per Python 2. Vedi i punti mancanti in Codepage Layout - è dove Python soffoca con famigerato ordinal not in range.

AGGIORNAMENTO 20150604 : Si vocifera che Python 3 abbia la surrogateescapestrategia di errore per codificare elementi in dati binari senza perdita di dati e arresti anomali, ma ha bisogno di test di conversione [binary] -> [str] -> [binary], per convalidare sia le prestazioni che l'affidabilità.

AGGIORNAMENTO 20170116 : Grazie al commento di Nearoo - c'è anche la possibilità di eliminare tutti i byte sconosciuti con backslashreplaceil gestore degli errori. 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 il supporto Unicode di Python per i dettagli.

AGGIORNAMENTO 20170119 : Ho deciso di implementare la decodifica di escape slash che funziona sia per Python 2 che per Python 3. Dovrebbe essere più lento della cp437soluzione, ma dovrebbe produrre 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'))

6
Sento davvero che Python dovrebbe fornire un meccanismo per sostituire i simboli mancanti e continuare.
Anatoly Techtonik,

@techtonik: Questo non funzionerà su un array come ha funzionato in python2.
user2284570

@ user2284570 intendi la lista? E perché dovrebbe funzionare su array? Soprattutto array di galleggianti ...
Anatoly Techtonik,

Puoi anche semplicemente ignorare gli errori Unicode con b'\x00\x01\xffsd'.decode('utf-8', 'ignore')in Python 3.
Antonis Kalou,

3
@anatolytechtonik C'è la possibilità di lasciare la sequenza di escape nella stringa e andare avanti: b'\x80abc'.decode("utf-8", "backslashreplace")risulterà '\\x80abc'. Questa informazione è stata presa dalla pagina di documentazione Unicode che sembra essere stata aggiornata dalla stesura di questa risposta.
Nearoo,

86

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

b'hello'.decode()

che equivale a

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

D'altra parte, in Python 2 , la codifica predefinita è la codifica stringa predefinita. Quindi, dovresti usare:

b'hello'.decode(encoding)

dov'è encodingla codifica che desideri.

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


41

Penso che tu voglia davvero 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.

Tra l'altro, il fatto che si fa materia è la ragione per cui Python spostato utilizzando due tipi diversi per i dati binari e di testo: non può convertire magicamente tra di loro, perché non conosce la codifica a meno che non gli si dice! L'unico modo in cui potresti sapere è leggere la documentazione di Windows (o leggerla qui).


3
open()funzione per i flussi di testo o Popen()se lo passi, universal_newlines=Truedecidi magicamente la codifica dei caratteri per te ( locale.getpreferredencoding(False)in Python 3.3+).
jfs,

2
'latin-1'è una codifica testuale con tutti i punti di codice impostati, quindi puoi usarla per leggere efficacemente una stringa di byte in qualunque tipo di stringa supportata da Python (così testualmente su Python 2, in Unicode per Python 3).
tripla il

@tripleee: 'latin-1'è un buon modo per ottenere il mojibake. Inoltre ci sono magiche sostituzioni su Windows: è sorprendentemente difficile reindirizzare i dati da un processo a un altro non modificato, ad esempio dir: \xb6-> \x14(l'esempio alla fine della mia risposta)
jfs

32

Impostare universal_newlines su True, ovvero

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

5
Ho usato questo metodo e funziona. Anche se, è solo indovinare la codifica in base alle preferenze dell'utente sul sistema, quindi non è robusto come alcune altre opzioni. Questo è ciò che sta facendo, facendo riferimento a docs.python.org/3.4/library/subprocess.html: "Se universal_newlines è True, [stdin, stdout e stderr] verranno aperti come flussi di testo in modalità newline universale utilizzando la codifica restituita dalla locale .getpreferredencoding (Falso)."
twasbrillig

Su 3.7 puoi (e dovresti) fare text=Trueinvece di universal_newlines=True.
Boris,

23

Mentre la risposta di @Aaron Maenpaa funziona, un utente ha recentemente chiesto :

C'è 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')


.decode()che utilizza 'utf-8'potrebbe non riuscire (l'output del comando potrebbe utilizzare una codifica di caratteri diversa o persino restituire una sequenza di byte non codificabile). Tuttavia, se l'input è ascii (un sottoinsieme di utf-8), allora .decode()funziona.
jfs il

23

Per interpretare una sequenza di byte come testo, devi conoscere la codifica dei caratteri corrispondente:

unicode_text = bytestring.decode(character_encoding)

Esempio:

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

lsIl comando può produrre output che non può essere interpretato come testo. I nomi dei file su Unix possono essere qualsiasi sequenza di byte tranne barra b'/'e zero b'\0':

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

Cercando di decodificare tale zuppa di byte usando i rilanci di codifica utf-8 UnicodeDecodeError.

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

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

I dati sono danneggiati ma il programma rimane ignaro del verificarsi di un errore.

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 pertanto chardetesiste un modulo in grado di indovinare la codifica dei caratteri. Un singolo script Python può utilizzare più codifiche di caratteri in luoghi diversi.


lsl'output può essere convertito in una stringa Python usando una os.fsdecode() funzione che riesce anche per nomi di file non decodificabili (usa sys.getfilesystemencoding()e surrogateescapegestore degli errori su Unix):

import os
import subprocess

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

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

Se si passa universal_newlines=Trueparametro subprocessutilizza quindi locale.getpreferredencoding(False)per decodificare byte, ad esempio, può essere cp1252su Windows.

Per decodificare il flusso di byte al volo, è io.TextIOWrapper() possibile utilizzare: esempio .

Comandi diversi possono usare codifiche di caratteri differenti per il loro output, ad esempio dircomando interno ( 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 l'API Unicode di Windows), ad esempio, '\xb6'possono essere sostituiti con '\x14'le mappe di codec cp437 di Pitone b'\x14'per controllare il carattere U + 0014 anziché U + 00B6 (¶). Per supportare nomi di file con caratteri Unicode arbitrari, vedere Decodificare l'output di PowerShell eventualmente contenente caratteri Unicode non ASCII in una stringa Python


16

Poiché questa domanda si sta effettivamente ponendo subprocesssull'output, hai a disposizione un approccio più diretto poiché Popenaccetta una parola chiave di 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 altri utenti è decodificare i byte in testo:

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

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

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

3
Oppure con Python 3.7 puoi passare text=Trueper decodificare stdin, stdout e stderr usando la codifica fornita (se impostata) o altrimenti il ​​sistema predefinito. Popen(['ls', '-l'], stdout=PIPE, text=True).
Boris,

La decodifica lsdell'output mediante utf-8codifica potrebbe non riuscire (vedere l'esempio nella mia risposta del 2016 ).
jfs,

1
@Boris: se encodingviene fornito un parametro, il textparametro viene ignorato.
jfs,

11

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'

6

Quando lavoro con i dati dei sistemi Windows (con \r\nterminazioni 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 (a \r\r\n), portando a righe vuote extra. Le funzioni di lettura del testo di Python normalmente normalizzano le terminazioni di riga in modo che solo le stringhe utilizzino \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.


Stavo cercando l' .replace("\r\n", "\n")aggiunta così a lungo. Questa è la risposta se si desidera eseguire il rendering HTML corretto.
mhlavacka,

5

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

6
Si può effettivamente concatenare tutti i .strip, .replace, .encode, ecc chiamate in una lista di comprensione e solo scorrere i lista una volta, invece di iterazione oltre cinque volte.
Taylor Edmiston,

1
@TaylorEdmiston Forse risparmia sull'allocazione ma il numero di operazioni rimarrebbe lo stesso.
JulienD,

5

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

Produzione:

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

5
1) Come diceva @bodangly, il controllo del tipo non è affatto pitonico. 2) La funzione che hai scritto si chiama " byte_to_str", il che implica che restituirà uno str, ma stampa solo il valore convertito e stampa un messaggio di errore se fallisce (ma non genera un'eccezione). Questo approccio è anche non pitonico e offusca la bytes.decodesoluzione che hai fornito.
cosmicFluke,

3

Da sys - Parametri e funzioni specifici del sistema :

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


3
La pipe per il sottoprocesso è già un buffer binario. La tua risposta non riesce a capire come ottenere un valore di stringa dal bytesvalore risultante .
Martijn Pieters

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

1
Mentre questo codice può rispondere alla domanda, fornire un contesto aggiuntivo riguardo a come e / o perché risolve il problema migliorerebbe il valore a lungo termine della risposta. Ricorda che stai rispondendo alla domanda per i lettori in futuro, non solo per la persona che chiede ora! Si prega di modificare la risposta di aggiungere una spiegazione, e dare un'indicazione di ciò si applicano le limitazioni e le assunzioni. Inoltre, non fa male menzionare perché questa risposta è più appropriata di altre.
Dev-iL

Una spiegazione sarebbe in ordine.
Peter Mortensen,

1

Per il tuo caso specifico di "esegui un comando shell e ottieni il suo output come testo anziché byte", su Python 3.7, dovresti usare subprocess.rune passare text=True(oltre capture_output=Truea catturare l'output)

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

textera chiamato universal_newlines, ed è stato modificato (bene, con aliasing) in Python 3.7. Se si desidera supportare le versioni di Python precedenti alla 3.7, passare universal_newlines=Trueinvece ditext=True


0

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.


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.