ความแตกต่างระหว่างสตริงและสตริงไบต์คืออะไร?


209

ฉันทำงานกับห้องสมุดที่คืนสตริงไบต์และฉันต้องการแปลงเป็นสตริง

แม้ว่าฉันจะไม่แน่ใจว่าความแตกต่างคืออะไรถ้ามี

คำตอบ:


260

สมมติว่า Python 3 (ใน Python 2 ความแตกต่างนี้มีความหมายน้อยกว่าเล็กน้อย) - สตริงเป็นลำดับของอักขระเช่นunicode codepoints ; นี่เป็นแนวคิดที่เป็นนามธรรมและไม่สามารถเก็บไว้ในดิสก์ได้โดยตรง สตริงไบต์เป็นลำดับไบต์ที่แปลกประหลาดสิ่งต่าง ๆ ที่สามารถเก็บไว้ในดิสก์ การทำแผนที่ระหว่างพวกเขาเป็นการเข้ารหัส - มีจำนวนมากเหล่านี้ (และเป็นไปไม่ได้มากมาย) - และคุณจำเป็นต้องรู้ว่าสิ่งใดที่นำไปใช้ในกรณีเฉพาะเพื่อทำการแปลงเนื่องจากการเข้ารหัสที่แตกต่างกันอาจทำแผนที่ไบต์เดียวกัน เป็นสตริงอื่น:

>>> b'\xcf\x84o\xcf\x81\xce\xbdo\xcf\x82'.decode('utf-16')
'蓏콯캁澽苏'
>>> b'\xcf\x84o\xcf\x81\xce\xbdo\xcf\x82'.decode('utf-8')
'τoρνoς'

เมื่อคุณทราบว่าจะใช้อันไหนคุณสามารถใช้.decode()วิธีการของสตริงไบต์เพื่อรับสตริงอักขระที่ถูกต้องจากด้านบน เพื่อความสมบูรณ์.encode()เมธอดของสตริงอักขระจะไปในทางตรงกันข้าม:

>>> 'τoρνoς'.encode('utf-8')
b'\xcf\x84o\xcf\x81\xce\xbdo\xcf\x82'

7
ชี้แจงสำหรับหลามผู้ใช้ 2 คน ได้แก่strประเภทเป็นเช่นเดียวกับbytesประเภท; คำตอบนี้เทียบเท่าการเปรียบเทียบunicodeประเภท (ไม่มีอยู่ใน Python 3) กับstrประเภท
craymichael

3
@ KshitijSaraogi ที่ไม่เป็นความจริง ประโยคทั้งหมดนั้นได้รับการแก้ไขและน่าเสียดายเล็กน้อย การเป็นตัวแทนในหน่วยความจำของstrวัตถุPython 3 นั้นไม่สามารถเข้าถึงได้หรือเกี่ยวข้องจากด้าน Python โครงสร้างข้อมูลเป็นเพียงลำดับของ codepoints ภายใต้PEP 393การเข้ารหัสภายในที่แน่นอนคือหนึ่งใน Latin-1, UCS2 หรือ UCS4 และการแทน utf-8 อาจถูกแคชหลังจากได้รับการร้องขอเป็นครั้งแรก แต่แม้กระทั่งรหัส C ก็ไม่จำเป็นต้องพึ่งพารายละเอียดภายในเหล่านี้
lvc

1
หากไม่สามารถเก็บไว้ในดิสก์โดยตรงดังนั้นจะเก็บไว้ในหน่วยความจำได้อย่างไร
z33k

2
@ พวกเขาจะต้องได้รับการเข้ารหัสอย่างใดอย่างหนึ่งภายในด้วยเหตุผลนั้น แต่นี่ไม่ใช่ expos3s ให้คุณจากรหัส Python เหมือนกับที่คุณไม่ต้องสนใจว่าจะเก็บเลขทศนิยมอย่างไร
lvc

1
@ChrisStryczynski เห็นความคิดเห็นข้างต้น - ตรวจสอบว่าพวกเขาเก็บไว้ในหน่วยความจำอย่างใดแต่รูปแบบที่เป็นนามธรรมอย่างชัดเจนออกไป อันที่จริงวันนี้มันสามารถเปลี่ยนแปลงได้ในช่วงอายุการใช้งานของโปรแกรมและจะแตกต่างกันระหว่างสตริงที่แตกต่างกันหรืออาจจะมากกว่าหนึ่ง (การเข้ารหัสบางอย่างจะแคช) ขึ้นอยู่กับตัวละครในพวกเขา - แต่ครั้งเดียวที่คุณต้องกังวลเกี่ยวกับ นั่นคือถ้าคุณกำลังแฮ็กข้อมูลของสตริง
lvc

390

สิ่งเดียวที่คอมพิวเตอร์สามารถจัดเก็บได้คือไบต์

ในการจัดเก็บทุกอย่างในคอมพิวเตอร์คุณต้องเข้ารหัสก่อนเช่นแปลงเป็นไบต์ ตัวอย่างเช่น:

  • หากคุณต้องการเก็บเพลงแรกคุณจะต้องเข้ารหัสโดยใช้MP3, WAVฯลฯ
  • หากคุณต้องการที่จะเก็บภาพก่อนอื่นคุณต้องเข้ารหัสโดยใช้PNG, JPEGฯลฯ
  • หากคุณต้องการที่จะเก็บข้อความก่อนอื่นคุณต้องเข้ารหัสโดยใช้ASCII, UTF-8ฯลฯ

MP3, WAV, PNG, JPEG, ASCIIและUTF-8เป็นตัวอย่างของการเข้ารหัส การเข้ารหัสเป็นรูปแบบที่ใช้แทนเสียงรูปภาพข้อความ ฯลฯ เป็นไบต์

ใน Python สตริงไบต์เป็นเพียง: ลำดับของไบต์ มันไม่สามารถอ่านได้โดยมนุษย์ ภายใต้ประทุนทุกอย่างจะต้องถูกแปลงเป็นสตริงไบต์ก่อนที่จะสามารถเก็บไว้ในคอมพิวเตอร์ได้

ในทางกลับกันสตริงอักขระที่มักจะเรียกว่า "สตริง" คือลำดับของอักขระ มันเป็นมนุษย์อ่านได้ สตริงอักขระไม่สามารถเก็บไว้ในคอมพิวเตอร์ได้โดยตรง แต่ต้องเข้ารหัสก่อน (แปลงเป็นสตริงไบต์) มีการเข้ารหัสหลายผ่านซึ่งเป็นสายอักขระสามารถแปลงเป็นสตริงไบต์เช่นมีและASCIIUTF-8

'I am a string'.encode('ASCII')

รหัสงูใหญ่ดังกล่าวข้างต้นจะเข้ารหัสสตริงโดยใช้การเข้ารหัส'I am a string' ASCIIผลลัพธ์ของโค้ดด้านบนจะเป็นสตริงไบต์ หากคุณพิมพ์, Python b'I am a string'จะแสดงเป็น อย่างไรก็ตามจำไว้ว่าสตริงไบต์ไม่สามารถอ่านได้โดยมนุษย์มันเป็นเพียงแค่ไพ ธ อนถอดรหัสมันASCIIเมื่อคุณพิมพ์มัน ใน Python สตริงไบต์จะถูกแทนด้วย a bตามด้วยสตริงไบต์ASCIIแทน

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

b'I am a string'.decode('ASCII')

'I am a string'รหัสข้างต้นจะส่งกลับสายเดิม

การเข้ารหัสและถอดรหัสเป็นการดำเนินการผกผัน ทุกอย่างจะต้องได้รับการเข้ารหัสก่อนที่จะสามารถเขียนลงดิสก์ได้และจะต้องถอดรหัสก่อนที่มนุษย์จะสามารถอ่านได้


59
Zenadix สมควรได้รับความชื่นชมบางอย่างที่นี่ หลังจากหลายปีที่ทำงานในสภาพแวดล้อมนี้เขาเป็นคำอธิบายแรกที่คลิกกับฉัน ฉันอาจสักบนแขนอีกข้างหนึ่งของฉัน (แขนข้างหนึ่งมี "ขั้นต่ำที่แน่นอนทุกผู้พัฒนาซอฟต์แวร์อย่างแน่นอนต้องรู้เกี่ยวกับ Unicode และชุดอักขระ (ไม่มีข้อแก้ตัว!) โดย Joel Spolsky"
neil.millikin

4
ยอดเยี่ยมอย่างแน่นอน สุวิมลและเข้าใจง่าย อย่างไรก็ตามฉันอยากจะพูดถึงว่าบรรทัดนี้ - "ถ้าคุณพิมพ์ Python จะแสดงว่า b'I am a string '" เป็นจริงสำหรับ Python3 สำหรับ Python2 ไบต์และ str เป็นสิ่งเดียวกัน
SRC

5
ฉันกำลังมอบรางวัลนี้ให้คุณเป็นคำอธิบายที่มนุษย์สามารถอ่านได้เพื่อให้ความกระจ่างในเรื่องนี้!
fedorqui 'ดังนั้นหยุดการทำร้าย'

3
คำตอบที่ดี สิ่งเดียวที่อาจอาจจะเพิ่มคือการชี้ให้เห็นอย่างชัดเจนมากขึ้นว่าในอดีตโปรแกรมเมอร์และการเขียนโปรแกรมภาษามีแนวโน้มที่จะชัดเจนหรือโดยปริยายคิดว่าลำดับไบต์และสตริง ASCII เป็นสิ่งเดียวกัน Python 3 ตัดสินใจที่จะทำลายสมมติฐานนี้อย่างชัดเจน IMHO อย่างถูกต้อง
nekomatic

4
ลิงก์ไปยังโพสต์ของ Joel ที่ถูกกล่าวถึงโดย @ neil.millikin ด้านบน: joelonsoftware.com/2003/10/08/ …
Kshitij Saraogi

14

หมายเหตุ:ฉันจะอธิบายคำตอบของฉันสำหรับ Python 3 ให้ละเอียดยิ่งขึ้นเนื่องจากการสิ้นสุดของ Python 2 ใกล้จะจบลง

ใน Python 3

bytesประกอบด้วยลำดับของค่าที่ไม่ได้ลงทะเบียน 8 บิตในขณะที่strประกอบด้วยลำดับของจุดรหัส Unicode ที่แสดงถึงตัวละครที่เป็นข้อความจากภาษามนุษย์

>>> # bytes
>>> b = b'h\x65llo'
>>> type(b)
<class 'bytes'>
>>> list(b)
[104, 101, 108, 108, 111]
>>> print(b)
b'hello'
>>>
>>> # str
>>> s = 'nai\u0308ve'
>>> type(s)
<class 'str'>
>>> list(s)
['n', 'a', 'i', '̈', 'v', 'e']
>>> print(s)
naïve

แม้ว่าbytesและstrดูเหมือนจะทำงานในลักษณะเดียวกันกรณีของพวกเขาจะไม่เข้ากันกับแต่ละอื่น ๆ เช่นbytesและstrกรณีที่ไม่สามารถใช้ร่วมกันกับผู้ประกอบการเช่นและ> +นอกจากนี้โปรดทราบว่าการเปรียบเทียบbytesและstrอินสแตนซ์ของความเท่าเทียมกันเช่นการใช้==จะมีการประเมินเสมอFalseแม้ว่าจะมีอักขระเหมือนกันทุกประการก็ตาม

>>> # concatenation
>>> b'hi' + b'bye' # this is possible
b'hibye'
>>> 'hi' + 'bye' # this is also possible
'hibye'
>>> b'hi' + 'bye' # this will fail
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't concat str to bytes
>>> 'hi' + b'bye' # this will also fail
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can only concatenate str (not "bytes") to str
>>>
>>> # comparison
>>> b'red' > b'blue' # this is possible
True
>>> 'red'> 'blue' # this is also possible
True
>>> b'red' > 'blue' # you can't compare bytes with str
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: '>' not supported between instances of 'bytes' and 'str'
>>> 'red' > b'blue' # you can't compare str with bytes
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: '>' not supported between instances of 'str' and 'bytes'
>>> b'blue' == 'red' # equality between str and bytes always evaluates to False
False
>>> b'blue' == 'blue' # equality between str and bytes always evaluates to False
False

ปัญหาอื่นเมื่อจัดการกับbytesและstrแสดงเมื่อทำงานกับไฟล์ที่ส่งคืนโดยใช้openฟังก์ชันในตัว ถ้าคุณต้องการอ่านหรือเขียนข้อมูลไบนารีไปยัง / จากไฟล์ให้เปิดไฟล์โดยใช้โหมดไบนารีเช่น 'rb' หรือ 'wb' เสมอ ในทางตรงกันข้ามหากคุณต้องการอ่านหรือเขียนข้อมูล Unicode ไปยัง / จากไฟล์ให้ระวังการเข้ารหัสเริ่มต้นของคอมพิวเตอร์ของคุณดังนั้นหากจำเป็นให้ส่งencodingพารามิเตอร์เพื่อหลีกเลี่ยงความประหลาดใจ

ใน Python 2

strประกอบด้วยลำดับของค่า 8 บิตในขณะที่unicodeประกอบด้วยลำดับของอักขระ Unicode สิ่งหนึ่งที่ต้องเก็บไว้ในใจก็คือว่าstrและunicodeสามารถใช้ร่วมกันกับผู้ประกอบการถ้าstrเพียงประกอบด้วยอักขระ ASCI 7 บิต

อาจเป็นประโยชน์ในการใช้ฟังก์ชันตัวช่วยเพื่อแปลงระหว่างstrและunicodeใน Python 2 และระหว่างbytesและstrใน Python 3


4

จากUnicode คืออะไร :

คอมพิวเตอร์จะจัดการกับตัวเลข พวกเขาเก็บตัวอักษรและตัวละครอื่น ๆ โดยการกำหนดหมายเลขสำหรับแต่ละคน

......

Unicode ให้หมายเลขที่ไม่ซ้ำกันสำหรับตัวละครทุกตัวไม่ว่าแพลตฟอร์มใดไม่ว่าจะเป็นโปรแกรมอะไรไม่ว่าจะเป็นภาษาใดก็ตาม

ดังนั้นเมื่อคอมพิวเตอร์แสดงถึงสตริงค้นหาอักขระที่เก็บอยู่ในคอมพิวเตอร์ของสตริงผ่านหมายเลข Unicode ที่ไม่ซ้ำกันและตัวเลขเหล่านี้จะถูกเก็บไว้ในหน่วยความจำ แต่คุณไม่สามารถเขียนสตริงลงดิสก์โดยตรงหรือส่งสตริงบนเครือข่ายผ่านหมายเลข Unicode ที่ไม่ซ้ำกันได้เพราะตัวเลขเหล่านี้เป็นเพียงเลขฐานสิบธรรมดา UTF-8คุณควรเข้ารหัสสตริงสตริงไบต์เช่น UTF-8เป็นการเข้ารหัสอักขระที่สามารถเข้ารหัสอักขระที่เป็นไปได้ทั้งหมดและจัดเก็บอักขระเป็นไบต์ (ดูเหมือนว่านี้ ) ดังนั้นสตริงที่เข้ารหัสสามารถใช้ได้ทุกที่เพราะUTF-8เกือบจะรองรับทุกที่ เมื่อคุณเปิดไฟล์ข้อความที่เข้ารหัสUTF-8จากระบบอื่นคอมพิวเตอร์ของคุณจะถอดรหัสและแสดงอักขระผ่านหมายเลข Unicode ที่ไม่ซ้ำกัน เมื่อเบราว์เซอร์ได้รับข้อมูลสตริงที่เข้ารหัสUTF-8จากเครือข่ายเบราว์เซอร์จะถอดรหัสข้อมูลเป็นสตริง (ถือว่าเบราว์เซอร์กำลังUTF-8เข้ารหัส) และแสดงสตริง

ใน python3 คุณสามารถแปลงสตริงและสตริงไบต์เป็น:

>>> print('中文'.encode('utf-8'))
b'\xe4\xb8\xad\xe6\x96\x87'
>>> print(b'\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8'))
中文 

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


1

Unicode เป็นรูปแบบที่ได้ตกลงกันไว้สำหรับการแสดงไบนารีของอักขระและการจัดรูปแบบต่าง ๆ (เช่นตัวพิมพ์เล็ก / ตัวพิมพ์ใหญ่, ขึ้นบรรทัดใหม่, การขึ้นบรรทัดใหม่), และ "สิ่งของ" อื่น ๆ (เช่น emojis) คอมพิวเตอร์ไม่สามารถจัดเก็บการแทนยูนิโค้ดได้ (ชุดของบิต) ไม่ว่าในหน่วยความจำหรือในไฟล์มากกว่าที่จะเก็บการแทน ASCII (ชุดของบิตที่แตกต่างกัน) หรือการแทนอื่น ๆ (ชุดของบิต )

สำหรับการสื่อสารที่จะเกิดขึ้นฝ่ายต่างๆในการสื่อสารจะต้องเห็นด้วยกับสิ่งที่จะใช้ในการเป็นตัวแทน

เนื่องจากยูนิโค้ดพยายามที่จะเป็นตัวแทนของตัวละครที่เป็นไปได้ทั้งหมด (และ "สิ่ง" อื่น ๆ ) ที่ใช้ในการสื่อสารระหว่างมนุษย์และระหว่างคอมพิวเตอร์จึงต้องใช้จำนวนบิตที่มากขึ้นสำหรับการเป็นตัวแทนของอักขระหลายตัว (หรือหลายสิ่ง) พยายามที่จะเป็นตัวแทนของตัวละคร / สิ่งที่ จำกัด เพื่อ "ลดความซับซ้อน" และบางทีเพื่อรองรับการใช้งานในอดีตการแทนยูนิโค้ดเกือบจะถูกแปลงเป็นระบบการแสดงอื่น ๆ (เช่น ascii) เพื่อวัตถุประสงค์ในการจัดเก็บอักขระในไฟล์

มันไม่ได้เป็นกรณีที่ Unicode ไม่สามารถนำมาใช้สำหรับการจัดเก็บตัวอักษรในไฟล์หรือส่งพวกเขาผ่านทางใดช่องทางสื่อสารเพียงแค่ว่ามันเป็นไม่ได้

คำว่า "string" ไม่ได้ถูกกำหนดอย่างแม่นยำ "String" ในการใช้งานทั่วไปหมายถึงชุดอักขระ / สิ่งต่าง ๆ ในคอมพิวเตอร์ตัวละครเหล่านั้นอาจถูกจัดเก็บในรูปแบบที่แตกต่างกันไปในแต่ละบิต "ไบต์สตริง" เป็นชุดของตัวละครที่เก็บไว้โดยใช้การเป็นตัวแทนที่ใช้แปดบิต (แปดบิตถูกเรียกว่าเป็นไบต์) ตั้งแต่วันนี้คอมพิวเตอร์ใช้ระบบ unicode (อักขระที่แสดงด้วยจำนวนตัวแปรไบต์) เพื่อเก็บอักขระไว้ในหน่วยความจำและสตริงไบต์ (อักขระที่แทนด้วยไบต์เดียว) เพื่อเก็บอักขระไว้ในไฟล์การแปลงจะต้องใช้ก่อนแสดงอักขระ ในหน่วยความจำจะถูกย้ายไปเก็บไว้ในไฟล์


0

มามีสตริงอักขระหนึ่งตัวที่ง่าย'š'และเข้ารหัสลงในลำดับไบต์:

>>> 'š'.encode('utf-8')
b'\xc5\xa1'

สำหรับจุดประสงค์ของตัวอย่างนี้เราจะแสดงลำดับของไบต์ในรูปแบบไบนารี่:

>>> bin(int(b'\xc5\xa1'.hex(), 16))
'0b1100010110100001'

ตอนนี้มันเป็นไปไม่ได้ที่จะถอดรหัสข้อมูลกลับโดยที่ไม่รู้ว่ามันถูกเข้ารหัสอย่างไร เฉพาะในกรณีที่คุณรู้ว่ามีการใช้การutf-8เข้ารหัสข้อความคุณสามารถทำตามอัลกอริทึมสำหรับการถอดรหัส utf-8และรับสตริงต้นฉบับ:

11000101 10100001
   ^^^^^   ^^^^^^
   00101   100001

คุณสามารถแสดงเลขฐานสอง101100001กลับเป็นสตริง:

>>> chr(int('101100001', 2))
'š'

0

ภาษา Python ประกอบด้วยstrและbytesเป็น "มาตรฐานชนิดที่มีอยู่ภายใน" กล่าวอีกนัยหนึ่งพวกเขาทั้งสองเรียน ฉันไม่คิดว่ามันคุ้มค่าที่จะพยายามหาเหตุผลว่าทำไม Python จึงถูกนำไปใช้ในวิธีนี้

ต้องบอกว่าstrและbytesมีความคล้ายคลึงกันมาก ทั้งสองวิธีส่วนใหญ่ใช้วิธีเดียวกันร่วมกัน วิธีการต่อไปนี้ไม่ซ้ำกันในstrชั้นเรียน:

casefold
encode
format
format_map
isdecimal
isidentifier
isnumeric
isprintable

วิธีการต่อไปนี้ไม่ซ้ำกันในbytesชั้นเรียน:

decode
fromhex
hex
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.