Perché ho bisogno di 'b' per codificare una stringa con Base64?


258

Seguendo questo esempio di Python , codifico una stringa come Base64 con:

>>> import base64
>>> encoded = base64.b64encode(b'data to be encoded')
>>> encoded
b'ZGF0YSB0byBiZSBlbmNvZGVk'

Ma, se lascio fuori il comando b:

>>> encoded = base64.b64encode('data to be encoded')

Ottengo il seguente errore:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Python32\lib\base64.py", line 56, in b64encode
   raise TypeError("expected bytes, not %s" % s.__class__.__name__)
   TypeError: expected bytes, not str

Perchè è questo?


38
In realtà tutte le domande che restituiscono "TypeError: byte previsti, non str" hanno la stessa risposta.
Lennart Regebro,

Risposte:


274

base64 codifica prende 8 bit byte di dati binari e codifica utilizza solo i caratteri A-Z, a-z, 0-9, +, /* in modo che possa essere trasmesso su canali che non conservano tutti gli 8 bit di dati, come la posta elettronica.

Quindi, vuole una stringa di byte a 8 bit. Li crei in Python 3 con la b''sintassi.

Se lo rimuovi b, diventa una stringa. Una stringa è una sequenza di caratteri Unicode. base64 non ha idea di cosa fare con i dati Unicode, non è a 8 bit. In realtà non è un po 'niente. :-)

Nel tuo secondo esempio:

>>> encoded = base64.b64encode('data to be encoded')

Tutti i caratteri si adattano perfettamente al set di caratteri ASCII e la codifica base64 è quindi in realtà un po 'inutile. Puoi invece convertirlo in ascii, con

>>> encoded = 'data to be encoded'.encode('ascii')

O più semplice:

>>> encoded = b'data to be encoded'

Sarebbe la stessa cosa in questo caso.


* La maggior parte degli aromi base64 può anche includere =a alla fine come imbottitura. Inoltre, alcune varianti di base64 possono utilizzare caratteri diversi da +e /. Consulta la tabella di riepilogo Varianti su Wikipedia per una panoramica.


174

Risposta breve

È necessario spingere un bytes-likeoggetto ( bytes, bytearray, ecc) per il base64.b64encode()metodo. Ecco due modi:

>>> data = base64.b64encode(b'data to be encoded')
>>> print(data)
b'ZGF0YSB0byBiZSBlbmNvZGVk'

O con una variabile:

>>> string = 'data to be encoded'
>>> data = base64.b64encode(string.encode())
>>> print(data)
b'ZGF0YSB0byBiZSBlbmNvZGVk'

Perché?

In Python 3, stroggetti non sono array di caratteri in stile C (in modo che siano non byte array), ma piuttosto, sono strutture di dati che non hanno alcuna codifica inerente. Puoi codificare quella stringa (o interpretarla) in vari modi. Il più comune (e predefinito in Python 3) è utf-8, soprattutto perché è retrocompatibile con ASCII (sebbene, come lo sono le codifiche più utilizzate). Questo è ciò che accade quando si prende un stringe si chiama il .encode()metodo su di esso: Python sta interpretando la stringa in utf-8 (la codifica predefinita) e ti fornisce l'array di byte a cui corrisponde.

Codifica Base 64 in Python 3

Inizialmente il titolo della domanda poneva domande sulla codifica Base-64. Continua a leggere per roba Base-64.

base64la codifica accetta blocchi binari a 6 bit e li codifica utilizzando i caratteri AZ, az, 0-9, '+', '/' e '=' (alcune codifiche usano caratteri diversi al posto di '+' e '/') . Questa è una codifica dei caratteri che si basa sul costrutto matematico del sistema numerico radix-64 o base-64, ma sono molto diversi. Base-64 in matematica è un sistema numerico come binario o decimale, e fai questo cambio di radix sull'intero numero o (se il radix da cui stai convertendo è una potenza di 2 in meno di 64) in blocchi da destra a sinistra.

Nella base64codifica, la traduzione viene eseguita da sinistra a destra; quei primi 64 caratteri sono il motivo per cui si chiama base64 codifica . Il 65 ° simbolo '=' viene utilizzato per il riempimento, poiché la codifica estrae blocchi di 6 bit ma i dati che di solito intende codificare sono byte di 8 bit, quindi a volte ci sono solo due o 4 bit nell'ultimo blocco.

Esempio:

>>> data = b'test'
>>> for byte in data:
...     print(format(byte, '08b'), end=" ")
...
01110100 01100101 01110011 01110100
>>>

Se interpretate quei dati binari come un singolo intero, allora è così che li convertireste in base-10 e base-64 ( tabella per base-64 ):

base-2:  01 110100 011001 010111 001101 110100 (base-64 grouping shown)
base-10:                            1952805748
base-64:  B      0      Z      X      N      0

base64 la codifica , tuttavia, raggrupperà nuovamente questi dati in questo modo:

base-2:  011101  000110  010101 110011 011101 00(0000) <- pad w/zeros to make a clean 6-bit chunk
base-10:     29       6      21     51     29      0
base-64:      d       G       V      z      d      A

Quindi, 'B0ZXN0' è la versione base 64 del nostro binario, matematicamente parlando. Tuttavia, la base64 codifica deve eseguire la codifica nella direzione opposta (quindi i dati grezzi vengono convertiti in 'dGVzdA') e ha anche una regola per dire ad altre applicazioni quanto spazio viene lasciato alla fine. Questo viene fatto riempiendo la fine con i simboli '='. Quindi, la base64codifica di questi dati è 'dGVzdA ==', con due simboli '=' per indicare due coppie di bit dovranno essere rimossi dalla fine quando questi dati vengono decodificati per farlo corrispondere ai dati originali.

Proviamo questo per vedere se sono disonesto:

>>> encoded = base64.b64encode(data)
>>> print(encoded)
b'dGVzdA=='

Perché usare la base64codifica?

Diciamo che devo inviare alcuni dati a qualcuno via e-mail, come questi dati:

>>> data = b'\x04\x6d\x73\x67\x08\x08\x08\x20\x20\x20'
>>> print(data.decode())

>>> print(data)
b'\x04msg\x08\x08\x08   '
>>>

Ci sono due problemi che ho piantato:

  1. Se provassi a inviare quell'e-mail in Unix, l'e-mail verrebbe inviata non appena il \x04carattere veniva letto, perché quello è ASCII per END-OF-TRANSMISSION(Ctrl-D), quindi i dati rimanenti sarebbero lasciati fuori dalla trasmissione.
  2. Inoltre, mentre Python è abbastanza intelligente da sfuggire a tutti i miei personaggi di controllo malvagio quando stampo direttamente i dati, quando quella stringa viene decodificata come ASCII, puoi vedere che "msg" non è presente. Questo perché ho usato tre BACKSPACEcaratteri e tre SPACEcaratteri per cancellare "msg". Pertanto, anche se non avessi il EOFpersonaggio lì, l'utente finale non sarebbe in grado di tradurre dal testo sullo schermo ai dati reali e non elaborati.

Questa è solo una demo per mostrare quanto sia difficile inviare semplicemente dati non elaborati. La codifica dei dati nel formato base64 fornisce esattamente gli stessi dati, ma in un formato che garantisce che sia sicuro per l'invio su supporti elettronici come l'e-mail.


6
base64.b64encode(s.encode()).decode()non è molto pythonic quando tutto ciò che vuoi è una conversione da stringa a stringa. base64.encode(s)dovrebbe essere sufficiente almeno in python3. Grazie per un'ottima spiegazione su stringhe e byte in pitone
MortenB,

2
@MortenB Sì, è strano, ma il lato positivo è molto chiaro cosa sta succedendo fintanto che l'ingegnere è consapevole della differenza tra matrici di byte e stringhe, poiché non esiste una singola mappatura (codifica) tra loro, come altre lingue assumere.
Greg Schmit,

3
@MortenB A proposito, base64.encode(s)non funzionerebbe in Python3; stai dicendo che qualcosa del genere dovrebbe essere disponibile? Penso che il motivo per cui potrebbe essere fonte di confusione è che, a seconda della codifica e del contenuto della stringa, spotrebbe non avere 1 rappresentazione univoca come matrice di byte.
Greg Schmit,

Schmitt: era solo un esempio di quanto dovrebbe essere semplice. i casi d'uso più comuni dovrebbero essere così.
MortenB,

1
@MortenB ma b64 non è pensato solo per il testo, qualsiasi contenuto binario può essere codificato in b64 (audio, immagini, ecc.). Renderlo funzionante come da me proposto a mio avviso nasconde ancora di più la differenza tra array di testo e byte, rendendo più difficile il debug. Sposta semplicemente la difficoltà da qualche altra parte.
Michael Ekoka,

32

Se i dati da codificare contengono caratteri "esotici", penso che devi codificare in "UTF-8"

encoded = base64.b64encode (bytes('data to be encoded', "utf-8"))

24

Se la stringa è Unicode il modo più semplice è:

import base64                                                        

a = base64.b64encode(bytes(u'complex string: ñáéíóúÑ', "utf-8"))

# a: b'Y29tcGxleCBzdHJpbmc6IMOxw6HDqcOtw7PDusOR'

b = base64.b64decode(a).decode("utf-8", "ignore")                    

print(b)
# b :complex string: ñáéíóúÑ

In realtà non è il modo più semplice, ma uno dei modi più chiari, quando è importante quale codifica viene utilizzata per trasmettere la stringa, che fa parte del "protocollo" della trasmissione dei dati attraverso base64.
xuiqzy,

12

C'è tutto ciò di cui hai bisogno:

expected bytes, not str

Il comando iniziale brende la stringa binaria.

Quale versione di Python usi? 2.xo 3.x?

Modifica: vedi http://docs.python.org/release/3.0.1/whatsnew/3.0.html#text-vs-data-instead-of-unicode-vs-8-bit per i dettagli cruenti delle stringhe in Python 3.x


Grazie che sto usando, 3.x. Perché Python vuole convertirlo esplicitamente in binario. Lo stesso in Ruby sarebbe ... richiede> "base64" e poi> Base64.encode64 ('data to be encoded')
dublintech,

2
@dublintech Perché il testo (unicode) è diverso dai dati non elaborati. Se si desidera codificare una stringa di testo in Base64, è necessario innanzitutto determinare la codifica dei caratteri (come UTF-8) e quindi disporre di byte anziché di caratteri, che è possibile codificare in un formato ASCII-safe.
Fortran,

2
Questo non risponde alla domanda. Sa che funziona con un oggetto byte, ma non un oggetto stringa. La domanda è perché .
Lennart Regebro,

@fortran La codifica predefinita della stringa Python3 è UTF, non so perché debba essere impostata in modo esplicito.
xmedeko,

0

Che b significa semplicemente che stai prendendo input come array di byte o byte e non come stringa.

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.