Base64 ile bir dizeyi kodlamak için neden 'b' ye ihtiyacım var?


260

Bu python örneğini izleyerek, bir dizeyi Base64 olarak kodlarım:

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

Ama, liderleri dışarıda bırakırsam b:

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

Aşağıdaki hatayı alıyorum:

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

Bu neden?


38
Aslında "TypeError: str değil beklenen bayt" döndüren tüm sorular aynı cevaba sahiptir.
Lennart Regebro

Yanıtlar:


275

base64 kodlama kullandığı 8-bit ikili bayt veri ve kodlamakta aldığı tek karakterler A-Z, a-z, 0-9, +, /* bu e-posta gibi tüm verileri 8-bit, korumaz kanallar üzerinden iletilmesi için.

Bu nedenle, 8 bit baytlık bir dizi ister. Bunları Python 3'te b''sözdizimi ile oluşturursunuz.

Kaldırırsanız, bbir dize olur. Dize, Unicode karakter dizisidir. base64, Unicode verileriyle ne yapacağına dair hiçbir fikre sahip değil, 8 bit değil. Aslında hiç bit değil. :-)

İkinci örneğinizde:

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

Tüm karakterler ASCII karakter kümesine düzgün bir şekilde sığar ve base64 kodlaması aslında biraz anlamsızdır. Bunun yerine ascii'ye dönüştürebilirsiniz.

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

Veya daha basit:

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

Bu durumda da aynı şey olur.


* Çoğu base64 aroması =sonunda dolgu olarak bir de içerebilir . Ayrıca, bazı base64 varyantları +ve dışındaki karakterleri kullanabilir /. Genel bakış için Wikipedia'daki Varyantlar özet tablosuna bakın .


174

Kısa cevap

Bir itmek için gereken bytes-likenesne ( bytes, bytearrayiçin, vs) base64.b64encode()yöntemiyle. İşte iki yol:

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

Veya bir değişkenle:

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

Neden?

Python 3'te, strnesneler C tarzı karakter dizileri değildir (bu nedenle bayt dizileri değildir ), aksine, herhangi bir doğal kodlaması olmayan veri yapılarıdır. Bu dizeyi çeşitli şekillerde kodlayabilirsiniz (veya yorumlayabilirsiniz). En yaygın (ve Python 3'te varsayılan), özellikle ASCII ile geriye dönük olarak uyumlu olduğu için (en yaygın kullanılan kodlamalar gibi) utf-8'dir. Bunu alıp yöntemini stringçağırdığınızda olan .encode()şey budur: Python, dizeyi utf-8'de (varsayılan kodlama) yorumlar ve karşılık gelen bayt dizisini sağlar.

Python 3'te Base-64 Kodlama

Başlangıçta soru başlığı Base-64 kodlaması hakkında sorular sordu. Base-64 işleri için okumaya devam edin.

base64kodlama 6 bitlik ikili parçaları alır ve AZ, az, 0-9, '+', '/' ve '=' karakterlerini kullanarak kodlar (bazı kodlamalar '+' ve '/' yerine farklı karakterler kullanır) . Bu, radix-64 veya base-64 sayı sisteminin matematiksel yapısına dayanan bir karakter kodlamasıdır, ancak bunlar çok farklıdır. Matematikteki Base-64, ikili veya ondalık gibi bir sayı sistemidir ve bu sayıdaki tüm sayıdaki yarıçap değişikliğini veya (dönüştürdüğünüz yarıçap 64'ten az bir güçse) sağdan ayrıldı.

In base64kodlama, çeviri soldan sağa doğru yapılır; bu ilk 64 karakter neden base64 kodlama olarak adlandırılır . 65 '=' sembolü dolgu için kullanılır, çünkü kodlama 6 bitlik parçalar çeker, ancak genellikle kodlaması amaçlanan veriler 8 bit bayttır, bu nedenle bazen son yığında sadece iki veya 4 bit vardır.

Misal:

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

Bu ikili verileri tek bir tamsayı olarak yorumlarsanız, bu şekilde base-10 ve base-64'e dönüştürürsünüz ( base-64 için tablo ):

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

base64 Ancak kodlama bu verileri bu şekilde yeniden gruplandıracaktır:

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

Yani, 'B0ZXN0' ikili, matematiksel olarak konuşulan temel-64 sürümüdür. Bununla birlikte, base64 kodlamanın kodlamayı ters yönde yapması gerekir (böylece ham veriler 'dGVzdA'ya dönüştürülür) ve ayrıca diğer uygulamalara sonunda ne kadar alan kaldığını söylemek için bir kuralı vardır. Bu, ucu '=' sembolleriyle doldurarak yapılır. Bu nedenle, base64bu verilerin kodlanması 'dGVzdA ==', iki 'bit' sembolünü gösteren iki '=' sembolü ile bu verilerin orijinal verilerle eşleşmesi için kod çözüldüğünde sondan çıkarılması gerekecektir.

Dürüst olup olmadığımı görmek için bunu test edelim:

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

Neden base64kodlama kullanılır?

Diyelim ki, bu veriler gibi birisine e-posta yoluyla bazı veriler göndermek zorundayım:

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

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

Diktiğim iki sorun var:

  1. Bu e-postayı Unix'te göndermeye çalışırsam, \x04karakter okunduğunda e-posta gönderilir , çünkü bu END-OF-TRANSMISSION(Ctrl-D) için ASCII olduğundan geri kalan veriler iletimin dışında bırakılır.
  2. Ayrıca, verileri doğrudan yazdırdığımda Python tüm kötü kontrol karakterlerimden kaçacak kadar akıllı olsa da, bu dize ASCII olarak çözüldüğünde, 'msg'nin orada olmadığını görebilirsiniz. Çünkü 'msg'yi silmek için üç BACKSPACEkarakter ve üç SPACEkarakter kullandım. Böylece, EOForada karakter olmasa bile , son kullanıcı ekrandaki metinden gerçek, ham verilere çeviremezdi.

Bu sadece ham veri göndermenin ne kadar zor olabileceğini gösteren bir demodur. Verilerin base64 biçimine kodlanması size tam olarak aynı verileri verir, ancak e-posta gibi elektronik ortamlar üzerinden gönderilmesini güvenli bir biçimde sağlar.


6
base64.b64encode(s.encode()).decode()tek istediğiniz bir dizeden dizeye dönüşüm olduğunda çok pitonik değildir. base64.encode(s)en azından python3'te yeterli olmalıdır. Python dizeleri ve baytları hakkında çok iyi bir açıklama için teşekkürler
MortenB

2
@MortenB Evet, garip, ancak mühendis, bayt ve dizeler dizileri arasındaki farkın farkında olduğu sürece ne olduğu çok açık, çünkü diğer diller gibi aralarında tek bir eşleme (kodlama) yok varsayalım.
Greg Schmit

3
@MortenB Bu arada, base64.encode(s)Python3'te çalışmaz; böyle bir şeyin mevcut olması gerektiğini mi söylüyorsun? Kafa karıştırıcı olabilir nedeni, kodlama ve dizenin içeriğine bağlı solarak, bir bayt dizisi olarak 1 benzersiz temsil olmayabilir olmasıdır.
Greg Schmit

Schmitt: Ne kadar basit olması gerektiğine sadece bir örnekti. en yaygın kullanım şekilleri böyle olmalıdır.
MortenB

1
@ MortenB ancak b64 yalnızca metin için değildir, herhangi bir ikili içerik b64 kodlu olabilir (ses, görüntü vb.). Benim görüşüme göre teklif yapmak, metin ve bayt dizisi arasındaki farkı daha da gizleyerek hata ayıklamayı zorlaştırır. Zorluğu başka bir yere taşır.
Michael Ekoka

32

Kodlanacak veriler "egzotik" karakterler içeriyorsa, "UTF-8" de kodlamanız gerektiğini düşünüyorum

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

25

Dize Unicode ise en kolay yol:

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: ñáéíóúÑ

Gerçekten en kolay yol değil, en açık yollardan biri, base64 yoluyla veri iletiminin "protokolünün" bir parçası olan dizeyi iletmek için hangi kodlamanın kullanılması önemli olduğunda.
xuiqzy

12

İhtiyacınız olan her şey var:

expected bytes, not str

Lider bdizenizi ikili yapar.

Hangi Python sürümünü kullanıyorsunuz? 2.x veya 3.x?

Düzenleme: Bkz http://docs.python.org/release/3.0.1/whatsnew/3.0.html#text-vs-data-instead-of-unicode-vs-8-bit Python dizeleri kanlı detaylar için 3.x


Teşekkürler, 3.x kullanıyorum. Python neden onu açıkça ikiliye dönüştürmek istiyor? Ruby'de de aynı şey ...> "base64" ve ardından> Base64.encode64 ('kodlanacak veri') gerektirir
dublintech

2
@dublintech Çünkü (unicode) metin ham verilerden farklı. Base64'te bir metin dizesini kodlamak istiyorsanız, önce karakter kodlamasını (UTF-8 gibi) belirlemeniz gerekir ve daha sonra metin olarak güvenli bir biçimde kodlayabileceğiniz karakterler yerine baytlara sahip olmanız gerekir.
fortran

2
Bu soruya cevap vermiyor. Bayt nesnesiyle çalıştığını, ancak bir dize nesnesiyle çalışmadığını biliyor. Soru neden .
Lennart Regebro

@fortran Varsayılan Python3 dize kodlaması UTF'dir, bilmiyorum, neden açıkça ayarlanması gerekir.
xmedeko

0

Bu b, girdiyi dize olarak değil bayt veya bayt dizisi olarak aldığınız anlamına gelir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.