在字符串字面量前面加上'b'字符是什么意思?

1377

显然,以下是有效的语法:

b'The string'

我想知道:

  1. 在字符串前面加上这个b字符是什么意思?
  2. 使用它有什么影响?
  3. 何时适合使用它?

我在这里的SO相关问题中发现了一个相关的问题,但那个问题是关于PHP的,它说明 b用来表示字符串是二进制的,而不是Unicode的,在从PHP版本<6迁移到PHP 6时需要兼容。 我认为这不适用于Python。

我在Python网站上找到了关于使用相同语法中的u字符指定Unicode字符串的文档。不幸的是,该文档没有提及在该文档中的任何地方都没有b字符。

另外,只是出于好奇,是否存在比bu更多功能的符号?


4
自Python 3.6版本以来,有一种非常有用的f-strings字符串格式化方法可供使用。例如,您可以通过以下方式打印出“Hello world”这个字符串:v = "world" print(f"Hello {v}")。还可以通过f"{2 * 5}"这种方式得到字符串"10"。使用f-strings是处理字符串的一种更好的方式。 - thanos.a
3
f-Strings还有一个很方便的调试功能,如果在变量后面但括号前面加上等号(=),就会输出“v=123”字符串,以显示正在打印的任何内容的名称。即使是表达式,所以f'{25=}'也会输出"25=10"。 - diamondsea
1
@diamondsea,该功能是在3.8版本中引入的。 - AcK
对于好奇心的部分:stringprefix :: = "r" | "u" | "R" | "U" | "f" | "F" | "fr" | "Fr" | "fR" | "FR" | "rf" | "rF" | "Rf" | "RF"bytesprefix::= "b" | "B" | "br" | "Br" | "bR" | "BR" | "rb" | "rB" | "Rb" | "RB"。[文档:字符串和字节文字](https://docs.python.org/3/reference/lexical_analysis.html#literals) - AcK
@thanos.a 这就是方法... - Eric Nelson
12个回答

1179

Python 3.x清楚地区分了以下类型:

如果您熟悉:

  • Java或C#,则将str视为String,将bytes视为byte []
  • SQL,则将str视为NVARCHAR,将bytes视为BINARYBLOB;
  • Windows注册表,则将str视为REG_SZ,将bytes视为REG_BINARY

如果您熟悉C(++),那么请忘记您所学习关于char和字符串的所有内容,因为字符不是字节。那个想法早就过时了。

当您想要表示文本时,请使用str

print('שלום עולם')

当您想要表示低级二进制数据,例如结构体时,请使用bytes

NaN = struct.unpack('>d', b'\xff\xf8\x00\x00\x00\x00\x00\x00')[0]

你可以将字符串encode为一个字节对象bytes
>>> '\uFEFF'.encode('UTF-8')
b'\xef\xbb\xbf'

而且你可以将bytes解码成str

>>> b'\xE2\x82\xAC'.decode('UTF-8')
'€'

但是你不能自由地混合这两种类型。

>>> b'\xEF\xBB\xBF' + 'Text with a UTF-8 BOM'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't concat bytes to str

b'...' 表示法有些令人困惑,因为它允许使用 ASCII 字符而不是十六进制数字来指定字节 0x01-0x7F。

>>> b'A' == b'\x41'
True

但是我必须强调,一个字符并不等同于一个字节

>>> 'A' == b'A'
False

在Python 2.x中

Python的3.0之前版本没有文本和二进制数据之间的区别。相反,有:

  • unicode = u'...' 字面值 = Unicode字符序列 = 3.x str
  • str = '...' 字面值 = 混淆的字节/字符序列
    • 通常是文本,以某种未指定的编码方式编码。
    • 但也用于表示二进制数据,如struct.pack输出。

为了简化从2.x到3.x的过渡,b'...'字面语法被倒退到Python 2.6中,以便区分二进制字符串(在3.x中应该是bytes)和文本字符串(在3.x中应该是str)。b前缀在2.x中不起作用,但告诉2to3脚本在3.x中不将其转换为Unicode字符串。

所以,是的,在Python中b'...'字面值具有与PHP中相同的目的。

另外,出于好奇,是否还有其他符号可以做其他事情?

< p > r 前缀创建原始字符串(例如,< code>r'\t' 是反斜杠+< code>t 而不是制表符),三引号< code>'''...''' 或< code>"""...""" 允许多行字符串文字。

9
谢谢!阅读这些句子后,我明白了:为便于从 Python 2.x 迁移到 3.x,b'...' 文字语法被倒移回 Python 2.6。此举允许区分二进制字符串(在 3.x 中应该是 bytes)和文本字符串(在 3.x 中应该是 str)。b 前缀在 2.x 中不起作用,但告诉 2to3 脚本不要在 3.x 中将其转换为 Unicode 字符串。 - tommy.carstensen
10
'A' == b'A' --> False这个检查真的很明显。其余部分都很好,但在那一点上,我还没有正确理解字节字符串并不是真正的文本 - Wildcard
27
'שלום עולם' == 'hello world' - Eli
11
我相信许多人正在寻找的是“b"some string".decode('UTF-8')”这一行。 - Marvin Thobejane
4
除了ubr之外,Python 3.6还引入了f-string用于字符串格式化。例如:f'The temperature is {tmp_value} Celsius' - Conchylicultor
显示剩余5条评论

541

引用Python 2.x文档的话:

'b'或'B'前缀在Python 2中被忽略;它表明字面值在Python 3中应该成为字节字面值(例如,在使用2to3自动转换代码时)。 'u'或'b'前缀后面可以跟一个'r'前缀。

Python 3文档指出:

字节字面值总是以“b”或“B”为前缀;它们生成bytes类型的实例而不是str类型。它们只能包含ASCII字符;数值大于或等于128的字节必须用转义表示。


9
听起来 Python 版本小于 3 的程序将会忽略这个额外字符。那么在Python 3中,什么情况下需要使用字节串而不是普通字符串? - Jesse Webb
7
@Gweebz - 如果你实际上是在使用特定编码而不是Unicode转义来输入字符串(例如b'\xff\xfe\xe12'而不是'\u32e1'),请注意。 - detly
8
实际上,如果你从__future__导入了unicode_literals,这将会“反转”这个特定字符串的行为(在Python 2.x中)。 - Romuald Brunet
94
个人认为,对引用文件进行一些通俗易懂的叙述会使这个回答更好。请注意不要改变原意,不要添加解释或其他非翻译内容。 - Hack-R
23
否则,这是给已经理解的人的答案。 - Rafael Eyng
显示剩余2条评论

42

b代表字节字符串。

字节是实际的数据。字符串是一种抽象。

如果你有一个多字符的字符串对象,然后你取了一个单独的字符,那么它就是一个字符串,并且根据编码方式,它可能超过1个字节大小。

如果使用字节字符串取1个字节,你将得到一个0-255之间的单个8位值,如果这些字符由于编码而大于1个字节,则该值可能不表示完整的字符。

说实话,除非我有特定的低级原因要使用字节,否则我会使用字符串。


31

从服务器端发送响应时,它会以字节类型的形式发送,因此在客户端中将以 b'Response from server' 的形式显示。

为了消除 b'....',只需使用以下代码:

服务器文件:

stri="Response from server"    
c.send(stri.encode())

客户档案:

print(s.recv(1024).decode())

它将会打印 Response from server


1
它并没有解答Jesse Webb所提出的问题! - Chandra Kanth
我是在说,如果不使用编码和解码方法,字符串输出将以b' '为前缀,因为Python将其视为字节类型而不是字符串类型。如果您不想获得类似b'...'的输出,请使用上述方法,就这样。你没明白吗? - Nani Chintha
实际上,这正是被问到的问题的标题的答案:Q:“b'x'是什么?”A:“它执行'x'.encode()”。这就是它的字面意思。问题的其余部分想要知道比这更多的内容,但标题已经得到了回答。 - Michael Erickson
2
@MichaelErickson 不,b'x' 并不是 "执行 'x'.encode()。它只是创建了一个相同类型的值。如果你不相信我,可以尝试评估 b'\u1000' == '\u1000'.encode() - Karl Knechtel

27

这个问题的答案是,它确实如此:

data.encode()

为了解码它(移除b,因为有时你不需要它),

使用:

data.decode()

7
这是不正确的。bytes 字符串按照不同的机制在编译时进行解释; 它们不是 data.encode() 的语法糖,也不会创建一个str,并且""中文本的解释不相同。特别地,例如b"\u1000" 不会以任何有意义的编码方式创建代表Unicode字符0x1000bytes对象; 它将创建一个存储数值[92, 117, 49, 48, 48, 48]bytes对象 - 对应于反斜杠、小写字母u、数字1和三个数字0。 - Karl Knechtel

14

这里是一个例子,在 Python 3.x 中如果没有 b ,会抛出一个 TypeError 异常。

>>> f=open("new", "wb")
>>> f.write("Hello Python!")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' does not support the buffer interface

添加一个b前缀可以解决这个问题。


13

它将其转换为bytes字面值(在2.x中为str),并且适用于2.6+。

r前缀会导致反斜杠被“不解释”(不会被忽略,而这种区别很重要)。


1
根据aix的回答中引用的文档,这听起来是错误的;在Python 3之外的版本中,b将被忽略。 - Jesse Webb
2
无论如何,在2.x中它都将是一个“str”,因此可以说它被忽略了。当您从“__future__”模块导入“unicode_literals”时,这种区别很重要。 - Ignacio Vazquez-Abrams
在Python 3版本之外,b将被忽略。在2.x中,它没有任何影响,因为在2.x中,“str”名称与“bytes”相同。 - Karl Knechtel

10

除了其他人已经说过的之外,需要注意的是Unicode中的单个字符可能由多个字节组成

Unicode的工作原理是采用旧的ASCII格式(7位代码,看起来像0xxx xxxx),并添加多字节序列,其中所有字节都以1开头(1xxx xxxx)表示ASCII之外的字符,使Unicode与ASCII向后兼容

>>> len('Öl')  # German word for 'oil' with 2 characters
2
>>> 'Öl'.encode('UTF-8')  # convert str to bytes 
b'\xc3\x96l'
>>> len('Öl'.encode('UTF-8'))  # 3 bytes encode 2 characters !
3

这是有用的补充信息,但它并没有回答问题。它应该作为评论写在另一个答案下面。 - Karl Knechtel
Unicode中的单个字符首先不由字节组成。一个Unicode字符在特定编码(如UTF-8、UTF-16、UTF-32或奇怪的编码,如UTF-7)可以由多个字节组成(对于其中一些编码,它们总是由多个字节组成),但Unicode字符是理论上的概念;它们没有固有的字节表示。 - ShadowRanger

8

b"hello"虽然看起来像字符串,但实际上它是一个字节序列而不是字符串。它是由5个数字组成的序列,如果将它们映射到字符表中,会看起来像h e l l o。然而,这个值本身并不是一个字符串,Python只是使用文本字符来定义字节序列的一种方便语法,而不是直接使用数字本身。这样做可以减少打字量,而且通常字节序列确实被解释为字符。但是,这并不总是正确的——例如,读取JPG文件将产生一系列无意义的字母,而这些字母位于b"..."之内,因为JPG具有非文本结构。

.encode().decode()在字符串和字节之间进行转换。


6

您可以使用JSON将其转换为字典

import json
data = b'{"key":"value"}'
print(json.loads(data))

{"key":"value"}


FLASK:

这是一个来自Flask的例子。在终端上运行以下命令:

import requests
requests.post(url='http://localhost(example)/',json={'key':'value'})

在 Flask 的 routes.py 文件中:
@app.route('/', methods=['POST'])
def api_script_add():
    print(request.data) # --> b'{"hi":"Hello"}'
    print(json.loads(request.data))
return json.loads(request.data)

{'键':'值'}


这个方法对JSON数据很有效,但对于其他类型的数据可能会失败。如果你有一个通用的str数据,比如XML,你可以将变量赋值并解码它。就像这样:data = request.data,然后data = data.decode() - Andrea
1
这并没有回答问题。问题是关于 b 的含义,而不是关于可以用对象做什么。此外,这只能在非常小的一部分 bytes 字面量中完成,即那些按照 JSON 规范格式化的字面量。 - Karl Knechtel
亲爱的@KarlKnechtel, 虽然它没有直接回答这个问题,但对于Stackoverflow的SEO来说是有好处的。如果有人遇到了这个问题,但无法形成正确的问题,只是提到类似b' Flask/Django之类的内容,那么这个答案将更相关,搜索引擎会将其放在前面。 - Karam Qusai

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接