วิธีที่ดีที่สุดในการแปลงสตริงเป็นไบต์ใน Python 3


860

ดูเหมือนจะมีสองวิธีในการแปลงสตริงเป็นไบต์ตามที่เห็นในคำตอบของTypeError: 'str' ไม่รองรับส่วนต่อประสานบัฟเฟอร์

วิธีใดต่อไปนี้จะดีกว่าหรือไพ ธ อน หรือมันเป็นเพียงเรื่องของการตั้งค่าส่วนตัว?

b = bytes(mystring, 'utf-8')

b = mystring.encode('utf-8')

42
ใช้การเข้ารหัส / ถอดรหัสเป็นเรื่องปกติและอาจชัดเจนกว่า
Lennart Regebro

11
@LennartRegebro ฉันเลิกจ้าง แม้ว่ามันจะเป็นเรื่องปกติมากขึ้นการอ่าน "ไบต์ ()" ฉันรู้ว่ามันทำอะไรขณะที่การเข้ารหัส () ไม่ทำให้ฉันรู้สึกว่ามันกำลังเข้ารหัสเป็นไบต์
m3nda

2
@ erm3nda ซึ่งเป็นเหตุผลที่ดีที่จะใช้มันจนกว่ามันจะรู้สึกอย่างนั้นแล้วคุณจะเข้าใกล้ Unicode Zen
Lennart Regebro

3
@ LennartRegebro ฉันรู้สึกดีพอที่จะใช้เพียงbytes(item, "utf8")เพราะชัดแจ้งดีกว่าโดยปริยายดังนั้น ... str.encode( )ค่าเริ่มต้นเงียบกับไบต์ทำให้คุณ Unicode-zen มากขึ้น แต่ Explicit-Zen น้อยลง นอกจากนี้ "ทั่วไป" ไม่ใช่คำที่ฉันชอบติดตาม นอกจากนี้ยังbytes(item, "utf8")เป็นเหมือนstr()และb"string"สัญลักษณ์ ฉันขอโทษถ้าฉันไม่เข้าใจเหตุผลของคุณ ขอบคุณ.
m3nda

4
@ erm3nda หากคุณอ่านคำตอบที่ยอมรับแล้วคุณจะเห็นencode()ว่าไม่ได้โทรbytes()มามันเป็นวิธีอื่น แน่นอนว่าไม่ชัดเจนในทันทีซึ่งเป็นสาเหตุที่ฉันถามคำถาม
Mark Ransom

คำตอบ:


571

หากคุณดูเอกสารbytesนั้นจะนำคุณไปที่bytearray:

bytearray ([แหล่ง [การเข้ารหัส [ข้อผิดพลาด]]])

ส่งกลับอาร์เรย์ไบต์ใหม่ ประเภท bytearray เป็นลำดับที่ไม่แน่นอนของจำนวนเต็มในช่วง 0 <= x <256 มันมีวิธีการปกติมากที่สุดของลำดับที่ไม่แน่นอนที่อธิบายไว้ในประเภทลำดับที่ไม่แน่นอนเช่นเดียวกับวิธีส่วนใหญ่ที่ประเภทไบต์มีดูไบต์และ วิธีการอาร์เรย์ไบต์

พารามิเตอร์ต้นทางที่เป็นทางเลือกสามารถใช้ในการเริ่มต้นอาร์เรย์ได้หลายวิธี:

หากเป็นสตริงคุณต้องให้พารามิเตอร์การเข้ารหัส (และเป็นทางเลือกข้อผิดพลาด) ด้วย bytearray () จากนั้นแปลงสตริงเป็นไบต์โดยใช้ str.encode ()

ถ้าเป็นจำนวนเต็มอาร์เรย์จะมีขนาดนั้นและจะถูกเตรียมใช้งานด้วยไบต์ว่าง

ถ้ามันเป็นวัตถุที่สอดคล้องกับส่วนต่อประสานบัฟเฟอร์บัฟเฟอร์แบบอ่านอย่างเดียวของวัตถุจะถูกใช้เพื่อเริ่มต้นอาร์เรย์ไบต์

ถ้าเป็นตัววนซ้ำมันต้องเป็นตัววนซ้ำในช่วง 0 <= x <256 ซึ่งใช้เป็นเนื้อหาเริ่มต้นของอาร์เรย์

หากไม่มีอาร์กิวเมนต์จะสร้างอาร์เรย์ขนาด 0 ขึ้นมา

ดังนั้นbytesสามารถทำได้มากกว่าการเข้ารหัสสตริง เป็น Pythonic ที่อนุญาตให้คุณเรียก Constructor ด้วยพารามิเตอร์ซอร์สชนิดใดก็ได้ที่สมเหตุสมผล

สำหรับการเข้ารหัสสตริงฉันคิดว่าsome_string.encode(encoding)มันเป็น Pythonic มากกว่าการใช้ Constructor เพราะมันเป็นการทำเอกสารด้วยตัวเองมากที่สุด - "รับสายนี้แล้วเข้ารหัสด้วยการเข้ารหัสนี้" ชัดเจนกว่าbytes(some_string, encoding)- ไม่มีกริยาที่ชัดเจนเมื่อคุณใช้ นวกรรมิก

แก้ไข:ฉันตรวจสอบแหล่ง Python ถ้าคุณผ่านสายอักขระ Unicode ที่จะbytesใช้ CPython มันเรียกPyUnicode_AsEncodedStringซึ่งเป็นการดำเนินงานของencode; ดังนั้นคุณแค่ข้ามระดับของการอ้อมไปถ้าคุณเรียกencodeตัวเองว่า

นอกจากนี้ดูความคิดเห็นของ Serdalis - unicode_string.encode(encoding)เป็น Pythonic มากกว่าเพราะการผกผันของมันbyte_string.decode(encoding)และสมมาตรนั้นดี


73
+1 สำหรับการมีอาร์กิวเมนต์ที่ดีและราคาจากเอกสารหลาม นอกจากนี้ยังunicode_string.encode(encoding)ตรงกับbytearray.decode(encoding)เมื่อคุณต้องการสตริงของคุณกลับมา
Serdalis

6
bytearrayจะใช้เมื่อคุณต้องการวัตถุที่ไม่แน่นอน คุณไม่จำเป็นต้องใช้สำหรับง่ายstrbytesแปลง
hamstergene

8
@EugeneHomyakov สิ่งนี้ไม่มีส่วนเกี่ยวข้องbytearrayยกเว้นว่าเอกสารสำหรับการbytesไม่ให้รายละเอียดพวกเขาเพียงแค่พูดว่า "นี่เป็นรุ่นที่ไม่เปลี่ยนรูปbytearray" ดังนั้นฉันต้องอ้างอิงจากที่นั่น
agf

1
เพียงแค่ข้อควรระวังจากPython ใน Nutshellเกี่ยวกับbytes: หลีกเลี่ยงการใช้ชนิดไบต์เป็นฟังก์ชันที่มีอาร์กิวเมนต์จำนวนเต็ม ใน v2 นี่จะคืนค่าจำนวนเต็มที่แปลงเป็นสตริง (ไบต์) เนื่องจากไบต์เป็นชื่อแทนสำหรับ str ในขณะที่ใน v3 จะส่งคืนการทดสอบโดยมีจำนวนอักขระ null ที่กำหนดไว้ ตัวอย่างเช่นแทนที่จะเป็นไบต์นิพจน์ v3 (6) ให้ใช้ค่า b '\ x00' * 6 ที่เทียบเท่าซึ่งทำงานในลักษณะเดียวกันในแต่ละรุ่นอย่างราบรื่น
holdenweb

2
เพียงแค่ทราบว่าถ้าคุณกำลังพยายามที่จะแปลงข้อมูลไบนารีสตริงคุณจะมีโอกาสมากที่สุดจำเป็นที่จะต้องใช้สิ่งที่ต้องการbyte_string.decode('latin-1')เป็นutf-8ไม่ครอบคลุมทั้งช่วง 0x00 เพื่อ 0xFF (0-255) ตรวจสอบหลามเอกสารสำหรับ ข้อมูลเพิ่มเติม.
iggy12345

348

ง่ายกว่าที่คิด:

my_str = "hello world"
my_str_as_bytes = str.encode(my_str)
type(my_str_as_bytes) # ensure it is byte representation
my_decoded_str = my_str_as_bytes.decode()
type(my_decoded_str) # ensure it is string representation

37
เขารู้วิธีที่จะทำเขาแค่ถามว่าวิธีไหนดีกว่า โปรดอ่านคำถามอีกครั้ง
agf

30
FYI: str.decode (bytes) ใช้งานไม่ได้สำหรับฉัน (Python 3.3.3 กล่าวว่า "type object 'str' ไม่มี attribute 'decode'") ฉันใช้ bytes.decode () แทน
Mike

6
@ ไมค์ใช้งาน: ใช้obj.method()ไวยากรณ์แทนcls.method(obj)ไวยากรณ์เช่นการใช้งานและbytestring = unicode_text.encode(encoding) unicode_text = bytestring.decode(encoding)
jfs

2
... เช่นคุณไม่จำเป็นต้องใช้วิธีที่ไม่ได้ผูกไว้แล้วเรียกมันว่าผ่านselfอาร์กิวเมนต์แรก
Antti Haapala

2
@KolobCanyon คำถามแสดงวิธีที่ถูกต้องแล้ว - เรียกencodeใช้เป็นวิธีการผูกไว้กับสตริง คำตอบนี้แนะนำว่าคุณควรเรียกเมธอด unbound และส่งผ่านสตริงนั้น นั่นเป็นข้อมูลใหม่เท่านั้นในคำตอบและมันผิด
abarnert

144

อย่างวิธีที่ดีที่สุดคือทั้ง 2 แต่ที่ 3 พารามิเตอร์แรกที่ใช้เป็นค่าเริ่มต้นนับตั้งแต่ Python 3.0 ดังนั้นวิธีที่ดีที่สุดคือencode 'utf-8'

b = mystring.encode()

นอกจากนี้ยังจะได้เร็วขึ้นเพราะการโต้เถียงเริ่มต้นส่งผลให้ไม่ได้อยู่ในสตริง"utf-8"ในรหัส C แต่NULLซึ่งเป็นมากได้เร็วขึ้นเพื่อตรวจสอบ!

นี่คือเวลาที่กำหนด:

In [1]: %timeit -r 10 'abc'.encode('utf-8')
The slowest run took 38.07 times longer than the fastest. 
This could mean that an intermediate result is being cached.
10000000 loops, best of 10: 183 ns per loop

In [2]: %timeit -r 10 'abc'.encode()
The slowest run took 27.34 times longer than the fastest. 
This could mean that an intermediate result is being cached.
10000000 loops, best of 10: 137 ns per loop

แม้จะมีการเตือนครั้งที่มีเสถียรภาพมากหลังจากวิ่งซ้ำ - ส่วนเบี่ยงเบนเป็นเพียงร้อยละ 2


ใช้encode()โดยไม่โต้แย้งไม่ได้เป็นงูหลาม 2 เข้ากันได้ในขณะที่งูหลาม 2 เข้ารหัสอักขระเริ่มต้นเป็นASCII

>>> 'äöä'.encode()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 0: ordinal not in range(128)

2
มีความแตกต่างเพียงเล็กน้อยที่นี่เพราะ (a) สตริงนั้นเป็น ASCII แท้หมายถึงที่เก็บข้อมูลภายในนั้นเป็นรุ่น UTF-8 อยู่แล้วดังนั้นการค้นหา codec นั้นเกือบจะเป็นราคาเพียงอย่างเดียวที่เกี่ยวข้องและ (b) สตริงนั้นเล็กมาก ดังนั้นแม้ว่าคุณจะต้องเข้ารหัสมันจะไม่สร้างความแตกต่างมากนัก ลองพูด'\u00012345'*10000ด้วย ทั้งคู่ใช้ 28.8us บนแล็ปท็อปของฉัน 50ns พิเศษอาจสูญหายไปในข้อผิดพลาดในการปัดเศษ แน่นอนว่านี่เป็นตัวอย่างที่ดีมาก - แต่'abc'ก็รุนแรงในทิศทางตรงกันข้าม
abarnert

@abertert เป็นความจริง แต่ถึงอย่างนั้นก็ไม่มีเหตุผลที่จะโต้แย้งอาร์กิวเมนต์เป็นสตริง
Antti Haapala

ตามนี้อาร์กิวเมนต์เริ่มต้นมักจะเป็น "วิธีที่ดีที่สุด" ในการทำสิ่งต่าง ๆ ใช่ไหม? การวิเคราะห์ความเร็วแบบนี้จะรู้สึกว่าเป็นการพูดเกินจริงที่น่าจะเป็นไปได้หากนี่เป็นเรื่องของรหัส C ในภาษาที่ตีความมันทำให้ฉันพูดไม่ออก
hmijail mourns ลาออก
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.