字符串和字节字符串有什么区别?


209

我正在使用一个返回字节字符串的库,我需要将其转换为字符串。

尽管我不确定有什么区别-如果有的话。

Answers:


260

假设使用Python 3(在Python 2中,这种区别的定义不太明确)-字符串是字符序列,即unicode码点;这些是一个抽象概念,不能直接存储在磁盘上。毫无疑问,字节字符串是字节序列,可以存储在磁盘上。它们之间的映射是一种编码 -其中有很多(并且无限可能)-并且您需要知道在特定情况下哪种适用才能进行转换,因为不同的编码可能会映射相同的字节到另一个字符串:

>>> 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
为了澄清Python 2用户:str类型与类型相同bytes;这个答案等效地将unicode类型(在Python 3中不存在)与str类型进行比较。
craymichael

3
@KshitijSaraogi也不太正确;整个句子都被编辑了,有点不幸。Python 3 str对象的内存中表示无法从Python端访问或相关。数据结构只是一系列代码点。在PEP 393下,确切的内部编码是Latin-1,UCS2或UCS4中的一种,并且在首次请求utf-8表示后可以对其进行缓存,但是甚至不鼓励C代码依赖于这些内部细节。
lvc

1
如果它们不能直接存储在磁盘上,那么它们如何存储在内存中呢?
z33k

2
正是由于这个原因,它们确实必须以某种方式在内部进行编码,但这并不是从Python代码中获得的结果,就像您不必关心浮点数的存储方式一样。
lvc

1
@ChrisStryczynski看到了上面的注释-确保它们以某种方式存储在内存中,但是该形式已被明确抽象。确实,这些天,它可能在程序的生命周期中发生变化,并且在不同的字符串之间可能有所不同,甚至可能不止一个(取决于某些编码,它们被缓存),这取决于它们中的字符,但这是您唯一需要担心的时间也就是说,如果您要破解字符串类型本身的实现。
lvc

390

计算机唯一可以存储的是字节。

要将任何内容存储在计算机中,必须先对其进行编码,即将其转换为字节。例如:

  • 如果你想存储的音乐,你必须先进行编码使用它MP3WAV等等。
  • 如果你想存储图片,必须先进行编码使用它PNGJPEG等等。
  • 如果你想存储文本,必须先进行编码使用它ASCIIUTF-8等等。

MP3WAVPNGJPEGASCIIUTF-8是的示例编码。编码是一种格式,以字节为单位表示音频,图像,文本等。

在Python中,字节字符串就是这样:字节序列。这不是人类可读的。在引擎盖下,必须先将所有内容转换为字节字符串,然后才能将其存储在计算机中。

另一方面,通常被称为“字符串”的字符串是字符序列。它是人类可读的。字符串不能直接存储在计算机中,必须先进行编码(转换为字节字符串)。可以通过多种编码将字符串转换为字节字符串,例如ASCIIUTF-8

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

上面的Python代码将'I am a string'使用encoding 对字符串进行编码ASCII。上面代码的结果将是一个字节字符串。如果您打印它,Python会将其表示为b'I am a string'。但是请记住,字节字符串不是人类可读的,只是Python从ASCII打印时就对其进行解码。在Python中,字节串由表示b,后跟字节串的ASCII表示。

如果您知道用于编码的字节,则可以将字节字符串解码回字符串。

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

上面的代码将返回原始字符串'I am a string'

编码和解码是相反的操作。在将所有内容写入磁盘之前,必须对其进行编码,并且必须对其进行解码,然后才能被人类读取。


59
Zenadix在这里值得赞扬。在这种环境下工作了几年后,他是我第一个点击的解释。我可能会在另一只手臂上纹身(一只手臂上已经有“ Joel Spolsky绝对绝对地肯定每个软件开发人员绝对肯定要知道的Unicode和字符集(无借口!)”)
neil.millikin

4
绝对辉煌​​。清醒易懂。但是,我想提一下这一行-“如果打印它,Python会将其表示为b'I is a string'”,对于Python3和Python2字节都是正确的,而str是同一回事。
SRC

5
我因提供了非常容易理解的解释来使您对此主题更加清楚而感到赏识!
fedorqui'SO停止伤害

3
好答案。唯一可以添加的是要更清楚地指出,从历史上看,程序员和编程语言倾向于显式或隐式地假定字节序列和ASCII字符串是同一件事。Python 3决定明确地打破这一假设,正确的恕我直言。
nekomatic

4
:链接到乔尔的帖子中提到的由@ neil.millikin上述joelonsoftware.com/2003/10/08/...
Kshitij Saraogi

14

注意:由于Python 2的生命周期即将结束,因此我将详细说明Python 3的答案。

在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

尽管bytesstr似乎相同的方式工作,他们的情况下,不与对方,即兼容,bytes并且str实例无法与像运营商一起使用>+。此外,请记住,比较bytesstr实例是否相等,即使用==,将始终计算为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

处理bytesstr使用使用返回的文件时存在的另一个问题open内置函数。一方面,如果要从文件读取二进制数据或从文件读取二进制数据,请始终使用“ rb”或“ wb”之类的二进制模式打开文件。另一方面,如果要从文件读取Unicode数据或从文件读取Unicode数据,请注意计算机的默认编码,因此如有必要,请传递encoding参数以避免意外情况。

在Python 2中

str由8位值unicode的序列组成,而由Unicode字符序列组成。有一点要记住的是,strunicode如果str仅由7位ASCI字符组成可以与运算符一起使用。

这可能是使用辅助功能之间进行转换有用的strunicode在Python 2之间,以及bytesstr在Python 3。


4

什么是Unicode

从根本上讲,计算机只处理数字。他们通过为每个字母分配一个数字来存储字母和其他字符。

......

无论平台是什么,程序是什么,语言是什么,Unicode都会为每个字符提供唯一的数字。

因此,当计算机表示字符串时,它会通过其唯一的Unicode数字找到存储在字符串计算机中的字符,并将这些数字存储在内存中。但是您不能直接将字符串写到磁盘或通过其唯一的Unicode数字在网络上传输字符串,因为这些数字只是简单的十进制数字。您应该将字符串编码为字节字符串,例如UTF-8UTF-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是一种公认​​的格式,用于字符的二进制表示和各种格式(例如,小写/大写,换行,回车)和其他“事物”(例如,表情符号)。无论是在内存中还是在文件中,计算机都能够存储unicode表示(一系列位),而不是存储ascii表示(一系列不同的位)或任何其他表示形式(一系列的位) )。

为了进行通讯通讯双方必须就将使用哪种表示形式达成一致。

因为unicode试图代表所有人与人之间和计算机间通信中使用的可能的字符(和其他“事物”),所以与许多其他表示系统相比,表示许多字符(或事物)所需要的位数更多。试图代表一组更有限的字符/事物。为了“简化”,并可能适应历史用法,unicode表示几乎专门转换为某种其他表示系统(例如ascii),目的是将字符存储在文件中。

这不是的情况下的unicode 不能被用于在文件中存储的字符,或通过发送它们的任何通信信道,只要它不。

术语“字符串”没有精确定义。通常,“字符串”是指一组字符/事物。在计算机中,这些字符可以以多种不同的逐位表示形式中的任何一种形式存储。“字节字符串”是一组字符,它们使用八位(八位称为字节)的表示形式存储。由于如今,计算机使用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语言包括strbytes作为标准的“内置类型”。换句话说,它们都是类。我认为尝试合理化以这种方式实现Python的理由并不值得。

话虽如此,str而且bytes彼此非常相似。两者共享大多数相同的方法。以下方法是str该类唯一的:

casefold
encode
format
format_map
isdecimal
isidentifier
isnumeric
isprintable

以下方法是bytes该类唯一的:

decode
fromhex
hex
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.