bytes()和b''之间的区别

4
我有以下的str:
"\xd0\xa0\xd0\xb0\xd1\x81\xd1\x88\xd0\xb8\xd1\x84\xd1\x80\xd0\xbe\xd0\xb2\xd0\xba\xd0\xb0_RootKit.com_63k.txt"

它来自于一个文件名:Расшифровка_RootKit.com_63k.txt

我的问题是无法将第一个str反转为第二个。我尝试了一些方法,使用en/decode()bytes()等,但我没有成功。

我注意到b''bytes()产生不同的输出:

path = "\xd0\xa0\xd0\xb0\xd1\x81\xd1\x88\xd0\xb8\xd1\x84\xd1\x80\xd0\xbe\xd0\xb2\xd0\xba\xd0\xb0_RootKit.com_63k.txt"
bpath = bytes(path, "UTF-8")
print(bpath.decode("UTF-8"))
print(b"\xd0\xa0\xd0\xb0\xd1\x81\xd1\x88\xd0\xb8\xd1\x84\xd1\x80\xd0\xbe\xd0\xb2\xd0\xba\xd0\xb0_RootKit.com_63k.txt".decode('utf8'))

结果:

РаÑÑиÑ
         Ñовка_RootKit.com_63k.txt
Расшифровка_RootKit.com_63k.txt

我想知道 b''bytes() 之间有什么区别,或许这可以帮助我解决我的问题!

4个回答

3

b'' 是一个前缀,会使后面的字符串被解释为 bytes 类型对象。函数 bytes 接受一个字符串并返回一个 bytes 对象。

print(b"\xd0\xa0\xd0\xb0\xd1\x81\xd1\x88\xd0\xb8\xd1\x84\xd1\x80\xd0\xbe\xd0\xb2\xd0\xba\xd0\xb0_RootKit.com_63k.txt".decode

这能够正常工作,因为您正在解码一个字节对象。
path = "\xd0\xa0\xd0\xb0\xd1\x81\xd1\x88\xd0\xb8\xd1\x84\xd1\x80\xd0\xbe\xd0\xb2\xd0\xba\xd0\xb0_RootKit.com_63k.txt"
bpath = bytes(path, "UTF-8")
print(bpath.decode("UTF-8"))

这样做并不会按照预期工作,因为您将path视为字符串,然后将其转换为字节对象,最后尝试对其进行解码。

好的,我现在明白了。谢谢。 - Chocorean
这并没有解释任何问题。即使这两个结果相同也不奇怪。Python的字符串和b''的解释方式不同,这就是结果不同的原因。请查看@awesoon的回答。 - starriet

3
< p >你可能希望使用latin1解决方案,首先滚动到该答案。如果您意外地将字节内容复制并粘贴为字符串,则此答案有效。

如果您想将它们转换回字节,请使用以下代码:

In [22]: path = "\xd0\xa0\xd0\xb0\xd1\x81\xd1\x88\xd0\xb8\xd1\x84\xd1\x80\xd0\xbe\xd0\xb2\xd0\xba\xd0\xb0_RootKit.com_63k.txt"

In [23]: bytes(map(ord, path)).decode('utf-8')
Out[23]: 'Расшифровка_RootKit.com_63k.txt'

解释很简单,让我们使用字符串的第一个字符:

In [40]: '\xd0'
Out[40]: 'Ð'

In [41]: b'\xd0'
Out[41]: b'\xd0'

您可以看到,字符串将\xd0转换为一个具有编号0xd0的Unicode字符,而字节则只将其解释为单个字节。
UTF-8对于所有介于U+0080U+07FF之间的字符使用以下掩码:第一个字节为110xxxxx,第二个字节为10xxxxxx。这正是将该字符串直接转换为字节时得到的结果。
In [43]: [bin(x) for x in '\xd0'.encode('utf-8')]
Out[43]: ['0b11000011', '0b10010000']

实际符号代码是00011+010000(连接,而不是加法),它是0xd0

In [44]: hex(int('00011010000', 2))
Out[44]: '0xd0'

要从字符中获取该数字,我们可以使用ord

In [45]: hex(ord('\xd0'))
Out[45]: '0xd0'

然后将其应用于整个字符串并将其转换回字节:
In [46]: bytes(map(ord, path)).decode('utf-8')
Out[46]: 'Расшифровка_RootKit.com_63k.txt'

请注意,如果由于某种原因您的字符串字符不符合字节大小,上述代码将引发错误:
In [47]: bytes([ord(chr(256))])
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-49-5555e18dbece> in <module>
----> 1 bytes([ord(chr(256))])

ValueError: bytes must be in range(0, 256)

太棒了,而且非常优雅!谢谢。 - Chocorean
@Chocorean更新了答案,并附上了解释和关于无效字符串的注释。 - awesoon
1
顺便说一句,我认为使用 latin1 的答案才是这个问题的真正答案。 - awesoon

2

要将您的字符串转换,只需使用“latin1”将其编码为字节,该编码方式具有字节和字符之间的一对一映射,并使用“utf8”进行解码:

s = "\xd0\xa0\xd0\xb0\xd1\x81\xd1\x88\xd0\xb8\xd1\x84\xd1\x80\xd0\xbe\xd0\xb2\xd0\xba\xd0\xb0_RootKit.com_63k.txt"

s.encode('latin1').decode('utf8')

# 'Расшифровка_RootKit.com_63k.txt'

1

path变量是一个字符串(不是字节)。 当您使用方法bytes()时,它会将其解码为字节,该方法将返回b'\xc3\x90\xc2\xa0\xc3\x90\xc2\xb0\xc3\x91\xc2\x81\xc3\x91\xc2\x88\xc3\x90\xc2\xb8\xc3\x91\xc2\x84\xc3\x91\xc2\x80\xc3\x90\xc2\xbe\xc3\x90\xc2\xb2\xc3\x90\xc2\xba\xc3\x90\xc2\xb0_RootKit.com_63k.txt'

但是,当您写b"\xd0\xa0\xd0\xb0\xd1\x81\xd1\x88\xd0\xb8\xd1\x84\xd1\x80\xd0\xbe\xd0\xb2\xd0\xba\xd0\xb0_RootKit.com_63k.txt"时,您引用的是Расшифровка_RootKit.com_63k.txt的字节值。


好的。有没有一种方法可以将我的字符串转换为字节而不解码它?(我知道这没有太多意义 -) - Chocorean
正如你所说"这没有太多意义",我仍在努力理解你想做什么?你的输入是什么,你想要的输出是什么? - Adirmola
我所说的文件已上传到Flask API,但它的名称变成了我的变量“path”,导致Flask无法保存该文件,因此我正在尝试将其重命名。 - Chocorean

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