Sự khác biệt giữa một chuỗi và một chuỗi byte là gì?


209

Tôi đang làm việc với một thư viện trả về một chuỗi byte và tôi cần chuyển đổi nó thành một chuỗi.

Mặc dù tôi không chắc sự khác biệt là gì - nếu có.

Câu trả lời:


260

Giả sử Python 3 (trong Python 2, sự khác biệt này được xác định rõ hơn một chút) - một chuỗi là một chuỗi các ký tự, tức là các điểm mã unicode ; đây là một khái niệm trừu tượng và không thể được lưu trữ trực tiếp trên đĩa. Chuỗi byte là một chuỗi, không có gì đáng ngạc nhiên, byte - những thứ có thể được lưu trữ trên đĩa. Ánh xạ giữa chúng là một mã hóa - có khá nhiều trong số này (và có thể có rất nhiều thứ có thể) - và bạn cần biết áp dụng trong trường hợp cụ thể để thực hiện chuyển đổi, vì một mã hóa khác có thể ánh xạ các byte giống nhau đến một chuỗi khác:

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

Khi bạn biết nên sử dụng cái nào, bạn có thể sử dụng .decode()phương thức của chuỗi byte để có được chuỗi ký tự đúng từ nó như trên. Để đầy đủ, .encode()phương thức của một chuỗi ký tự đi theo cách ngược lại:

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

7
Để làm rõ cho người dùng Python 2: strloại giống như bytesloại; câu trả lời này tương đương so sánh unicodeloại (không tồn tại trong Python 3) với strloại.
craymichael

2
@KshitijSaraogi cũng không hoàn toàn đúng; toàn bộ câu đã được chỉnh sửa và hơi đáng tiếc. Biểu diễn trong bộ nhớ của strcác đối tượng Python 3 không thể truy cập hoặc có liên quan từ phía Python; cấu trúc dữ liệu chỉ là một chuỗi các điểm mã. Theo PEP 393 , mã hóa nội bộ chính xác là một trong số Latin-1, UCS2 hoặc UCS4 và đại diện utf-8 có thể được lưu trữ sau khi được yêu cầu lần đầu, nhưng ngay cả mã C cũng không được khuyến khích dựa vào các chi tiết nội bộ này.
lvc

1
Nếu chúng không thể được lưu trữ trực tiếp trên đĩa, vậy chúng được lưu trữ trong bộ nhớ như thế nào?
z33k

1
@orety họ phải được mã hóa bằng cách nào đó bên trong vì lý do chính xác đó, nhưng đây không phải là expos3 cho bạn từ mã Python giống như bạn không phải quan tâm đến cách lưu trữ số dấu phẩy động.
lvc

1
@ChrisStryczynski xem các bình luận ở trên - chắc chắn rằng chúng được lưu trữ trong bộ nhớ bằng cách nào đó , nhưng hình thức đó được tóm tắt rõ ràng. Thật vậy, ngày nay, nó có thể thay đổi trong suốt thời gian tồn tại của một chương trình và khác nhau giữa các chuỗi khác nhau hoặc thậm chí có thể nhiều hơn một (một số mã hóa được lưu trữ), tùy thuộc vào các ký tự trong chúng - nhưng thời gian duy nhất bạn cần lo lắng về đó là nếu bạn đang hack việc thực hiện kiểu chuỗi.
lvc

390

Thứ duy nhất mà máy tính có thể lưu trữ là byte.

Để lưu trữ bất cứ thứ gì trong máy tính, trước tiên bạn phải mã hóa nó, tức là chuyển đổi nó thành byte. Ví dụ:

  • Nếu bạn muốn để lưu trữ nhạc, trước tiên bạn phải mã hóa nó bằng cách sử MP3, WAVvv
  • Nếu bạn muốn lưu trữ một bức tranh, trước tiên bạn phải mã hóa nó bằng cách sử PNG, JPEGvv
  • Nếu bạn muốn lưu trữ văn bản, trước tiên bạn phải mã hóa nó bằng cách sử ASCII, UTF-8vv

MP3, WAV, PNG, JPEG, ASCIIUTF-8là ví dụ về mã hóa . Mã hóa là một định dạng để thể hiện âm thanh, hình ảnh, văn bản, vv theo byte.

Trong Python, một chuỗi byte chỉ là: một chuỗi các byte. Nó không phải là con người có thể đọc được. Trong mui xe, mọi thứ phải được chuyển đổi thành một chuỗi byte trước khi nó có thể được lưu trữ trong máy tính.

Mặt khác, một chuỗi ký tự, thường chỉ được gọi là "chuỗi", là một chuỗi các ký tự. Nó là con người có thể đọc được. Một chuỗi ký tự không thể được lưu trữ trực tiếp trong máy tính, nó phải được mã hóa trước tiên (được chuyển đổi thành chuỗi byte). Có nhiều mã hóa thông qua đó một chuỗi ký tự có thể được chuyển đổi thành một chuỗi byte, chẳng hạn như ASCIIUTF-8.

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

Mã Python ở trên sẽ mã hóa chuỗi 'I am a string'bằng mã hóa ASCII. Kết quả của đoạn mã trên sẽ là một chuỗi byte. Nếu bạn in nó, Python sẽ đại diện cho nó là b'I am a string'. Tuy nhiên, hãy nhớ rằng các chuỗi byte không thể đọc được bằng con người , chỉ là Python giải mã chúng từ ASCIIkhi bạn in chúng. Trong Python, một chuỗi byte được biểu thị bằng a b, theo sau là ASCIIbiểu diễn của chuỗi byte .

Một chuỗi byte có thể được giải mã trở lại thành một chuỗi ký tự, nếu bạn biết mã hóa được sử dụng để mã hóa nó.

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

Đoạn mã trên sẽ trả về chuỗi gốc 'I am a string'.

Mã hóa và giải mã là các hoạt động nghịch đảo. Tất cả mọi thứ phải được mã hóa trước khi nó có thể được ghi vào đĩa và nó phải được giải mã trước khi con người có thể đọc được.


59
Zenadix xứng đáng với một số danh hiệu ở đây. Sau vài năm hoạt động trong môi trường này, anh ấy là lời giải thích đầu tiên nhấp vào tôi. Tôi có thể xăm nó lên cánh tay kia của mình (một cánh tay đã có "Tối thiểu tuyệt đối mọi nhà phát triển phần mềm Tuyệt đối, phải biết tích cực về bộ Unicode và bộ ký tự (Không có lý do!) Của Joel Spolsky"
neil.millikin 16/07/2015

4
Hoàn toàn rực rỡ. Lucid và dễ hiểu. Tuy nhiên, tôi muốn đề cập rằng dòng này - "Nếu bạn in nó, Python sẽ biểu thị nó là b'I am a string '" đúng với Python3 như đối với byte Python2 và str là điều tương tự.
SRC

5
Tôi đang trao thưởng cho bạn tiền thưởng này vì đã đưa ra một lời giải thích rất dễ đọc cho con người để đưa ra một số sự rõ ràng trong chủ đề này!
fedorqui 'SO ngừng làm hại'

3
Câu trả lời chính xác. Điều duy nhất có lẽ có thể được thêm vào là chỉ ra rõ ràng hơn rằng trong lịch sử, các lập trình viên và ngôn ngữ lập trình có xu hướng rõ ràng hoặc ngầm định rằng một chuỗi byte và chuỗi ASCII là giống nhau . Python 3 đã quyết định phá vỡ giả định này một cách rõ ràng, chính xác là IMHO.
nekomatic

4
Liên kết đến bài đăng của Joel được đề cập bởi @ neil.millikin ở trên: joelonsoftware.com/2003/10/08/ Lời
Kshitij Saraogi

14

Lưu ý: Tôi sẽ giải thích thêm câu trả lời của tôi cho Python 3 vì thời gian kết thúc của Python 2 rất gần.

Trong Python 3

bytesbao gồm các chuỗi các giá trị không dấu 8 bit, trong khi strbao gồm các chuỗi các điểm mã Unicode đại diện cho các ký tự văn bản từ ngôn ngữ của con người.

>>> # 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

Mặc dù bytesstrdường như hoạt động theo cùng một cách, các thể hiện của chúng không tương thích với nhau, nghĩa là, bytesvà các strthể hiện không thể được sử dụng cùng với các toán tử như >+. Ngoài ra, hãy nhớ rằng so sánh bytesvà các strtrường hợp cho sự bình đẳng, tức là sử dụng ==, sẽ luôn luôn đánh giá Falsengay cả khi chúng chứa chính xác các ký tự.

>>> # 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

Một vấn đề khác khi xử lý bytesstrcó mặt khi làm việc với các tệp được trả về bằng openhàm tích hợp. Một mặt, nếu bạn muốn đọc hoặc ghi dữ liệu nhị phân vào / từ một tệp, luôn mở tệp bằng chế độ nhị phân như 'rb' hoặc 'wb'. Mặt khác, nếu bạn muốn đọc hoặc ghi dữ liệu Unicode đến / từ một tệp, hãy lưu ý mã hóa mặc định của máy tính của bạn, vì vậy nếu cần, hãy chuyển encodingtham số để tránh gây bất ngờ.

Trong Python 2

strbao gồm các chuỗi các giá trị 8 bit, trong khi unicodebao gồm các chuỗi các ký tự Unicode. Một điều cần lưu ý là strunicodecó thể được sử dụng cùng với các toán tử nếu strchỉ bao gồm các ký tự ASCI 7 bit.

Có thể hữu ích khi sử dụng các hàm trợ giúp để chuyển đổi giữa strunicodetrong Python 2 và giữa bytesstrtrong Python 3.


4

Từ Unicode là gì :

Về cơ bản, máy tính chỉ cần đối phó với những con số. Họ lưu trữ các chữ cái và các ký tự khác bằng cách gán một số cho mỗi cái.

......

Unicode cung cấp một số duy nhất cho mỗi ký tự, bất kể nền tảng nào, bất kể chương trình nào, bất kể ngôn ngữ nào.

Vì vậy, khi một máy tính đại diện cho một chuỗi, nó sẽ tìm thấy các ký tự được lưu trữ trong máy tính của chuỗi thông qua số Unicode duy nhất của chúng và các số liệu này được lưu trữ trong bộ nhớ. Nhưng bạn không thể trực tiếp ghi chuỗi vào đĩa hoặc truyền chuỗi trên mạng thông qua số Unicode duy nhất của chúng vì những số liệu này chỉ là số thập phân đơn giản. Bạn nên mã hóa chuỗi thành chuỗi byte, chẳng hạn như UTF-8. UTF-8là một mã hóa ký tự có khả năng mã hóa tất cả các ký tự có thể và nó lưu trữ các ký tự dưới dạng byte (trông giống như thế này ). Vì vậy, chuỗi được mã hóa có thể được sử dụng ở mọi nơi vì UTF-8gần như được hỗ trợ ở mọi nơi. Khi bạn mở một tệp văn bản được mã hóa trongUTF-8từ các hệ thống khác, máy tính của bạn sẽ giải mã nó và hiển thị các ký tự trong đó thông qua số Unicode duy nhất của chúng. Khi một trình duyệt nhận dữ liệu chuỗi được mã hóa UTF-8từ mạng, nó sẽ giải mã dữ liệu thành chuỗi (giả sử trình duyệt UTF-8mã hóa) và hiển thị chuỗi.

Trong python3, bạn có thể chuyển đổi chuỗi và chuỗi byte sang nhau:

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

Trong một từ, chuỗi là để hiển thị cho con người để đọc trên máy tính và chuỗi byte là để lưu trữ vào đĩa và truyền dữ liệu.


1

Unicode là định dạng được thỏa thuận cho biểu diễn nhị phân của các ký tự và các loại định dạng khác nhau (ví dụ: chữ thường / chữ in hoa, dòng mới, trả lại vận chuyển) và các "thứ" khác (ví dụ: biểu tượng cảm xúc). Một máy tính không kém khả năng lưu trữ một đại diện unicode (một chuỗi bit), cho dù trong bộ nhớ hoặc trong một tệp, so với việc lưu trữ một đại diện ascii (một chuỗi bit khác nhau) hoặc bất kỳ đại diện nào khác (chuỗi bit ).

Để giao tiếp diễn ra, các bên tham gia giao tiếp phải thống nhất về việc đại diện nào sẽ được sử dụng.

Bởi vì unicode tìm cách đại diện cho tất cả các ký tự có thể (và các "thứ" khác) được sử dụng trong giao tiếp giữa người và máy tính, nên nó đòi hỏi số bit lớn hơn để thể hiện nhiều ký tự (hoặc vật) so với các hệ thống biểu diễn khác tìm cách đại diện cho một tập hợp các nhân vật / thứ giới hạn hơn. Để "đơn giản hóa" và có lẽ để phù hợp với việc sử dụng lịch sử, biểu diễn unicode gần như được chuyển đổi thành một số hệ thống biểu diễn khác (ví dụ ascii) cho mục đích lưu trữ các ký tự trong tệp.

Đây không phải là trường hợp unicode không thể được sử dụng để lưu trữ các ký tự trong tệp hoặc truyền chúng qua bất kỳ kênh liên lạc nào , đơn giản không phải vậy.

Thuật ngữ "chuỗi" không được xác định chính xác. "Chuỗi", theo cách sử dụng phổ biến của nó, đề cập đến một tập hợp các ký tự / vật. Trong máy tính, các ký tự đó có thể được lưu trữ trong bất kỳ một trong nhiều cách biểu diễn từng bit khác nhau. "Chuỗi byte" là một tập hợp các ký tự được lưu trữ bằng cách sử dụng một biểu diễn sử dụng tám bit (tám bit được gọi là một byte). Vì ngày nay, máy tính sử dụng hệ thống unicode (ký tự được biểu thị bằng số byte thay đổi) để lưu trữ các ký tự trong bộ nhớ và chuỗi byte (ký tự được biểu thị bằng byte đơn) để lưu trữ các ký tự vào tệp, phải sử dụng chuyển đổi trước khi ký tự được biểu thị trong bộ nhớ sẽ được chuyển vào lưu trữ trong các tập tin.


0

Chúng ta hãy có một chuỗi một ký tự đơn giản 'š'và mã hóa nó thành một chuỗi byte:

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

Với mục đích của ví dụ này, hãy hiển thị chuỗi byte ở dạng nhị phân của nó:

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

Bây giờ nói chung không thể giải mã thông tin trở lại mà không biết làm thế nào nó được mã hóa. Chỉ khi bạn biết rằng utf-8mã hóa văn bản đã được sử dụng, bạn có thể theo thuật toán để giải mã utf-8 và có được chuỗi gốc:

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

Bạn có thể hiển thị số nhị phân 101100001trở lại dưới dạng chuỗi:

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

0

Các ngôn ngữ Python bao gồm strbytesdưới dạng "Các loại tích hợp" tiêu chuẩn. Nói cách khác, cả hai đều là lớp. Tôi không nghĩ rằng đáng để cố gắng hợp lý hóa lý do tại sao Python được thực hiện theo cách này.

Phải nói rằng, strbytesrất giống nhau. Cả hai đều chia sẻ hầu hết các phương pháp giống nhau. Các phương thức sau là duy nhất cho strlớp:

casefold
encode
format
format_map
isdecimal
isidentifier
isnumeric
isprintable

Các phương thức sau là duy nhất cho byteslớp:

decode
fromhex
hex
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.