将字节转换为字符串
问:
我将外部程序的标准输出捕获到 bytes 对象中:
>>> from subprocess import *
>>> command_stdout = Popen(['ls', '-l'], stdout=PIPE).communicate()[0]
>>>
>>> command_stdout
b'total 0\n-rw-rw-r-- 1 thomas thomas 0 Mar 3 07:03 file1\n-rw-rw-r-- 1 thomas thomas 0 Mar 3 07:03 file2\n'
我想把它转换成一个普通的 Python 字符串,这样我就可以像这样打印它:
>>> print(command_stdout)
-rw-rw-r-- 1 thomas thomas 0 Mar 3 07:03 file1
-rw-rw-r-- 1 thomas thomas 0 Mar 3 07:03 file2
我尝试了 binascii.b2a_qp() 方法,但又得到了相同的 bytes 对象:
>>> binascii.b2a_qp(command_stdout)
b'total 0\n-rw-rw-r-- 1 thomas thomas 0 Mar 3 07:03 file1\n-rw-rw-r-- 1 thomas thomas 0 Mar 3 07:03 file2\n'
如何使用 Python 3 将 bytes 对象转换为 str?
答1:
huntsbot.com全球7大洲远程工作机会,探索不一样的工作方式
Decode the bytes object 产生一个字符串:
>>> b"abcde".decode("utf-8")
'abcde'
上面的示例假设 bytes 对象是 UTF-8,因为它是一种常见的编码。但是,您应该使用数据实际所在的编码!
使用 "windows-1252" 也不可靠(例如,对于其他语言版本的 Windows),使用 sys.stdout.encoding 不是最好吗?
也许这将进一步帮助某人:有时您使用字节数组进行前 TCP 通信。如果要将字节数组转换为字符串以截断尾随 '\x00' 字符,则以下答案是不够的。然后使用 b'example\x00\x00'.decode('utf-8').strip('\x00') 。
我在 bugs.python.org/issue17860 填写了一个关于记录它的错误 - 请随时提出补丁。如果很难做出贡献 - 欢迎评论如何改进。
在 Python 2.7.6 中不处理 b"\x80\x02\x03".decode("utf-8") -> UnicodeDecodeError: 'utf8' codec can't decode byte 0x80 in position 0: invalid start byte。
如果内容是随机二进制值,则 utf-8 转换可能会失败。请参阅@techtonik 答案(如下)stackoverflow.com/a/27527728/198536
答2:
与HuntsBot一起,探索全球自由职业机会–huntsbot.com
解码字节字符串并将其转换为字符 (Unicode) 字符串。
蟒蛇 3:
encoding = 'utf-8'
b'hello'.decode(encoding)
或者
str(b'hello', encoding)
蟒蛇2:
encoding = 'utf-8'
'hello'.decode(encoding)
或者
unicode('hello', encoding)
huntsbot.com – 程序员副业首选,一站式外包任务、远程工作、创意产品分享订阅平台。
在 Python 3 上,如果字符串在变量中怎么办?
@AlaaM .:一样。如果您有 variable = b'hello',那么 unicode_text = variable.decode(character_encoding)
对我来说,variable = variable.decode() 自动将其转换为我想要的字符串格式。
@亚历克斯霍尔>首先,您可能有兴趣知道 automagic 使用 utf8,如果您不提供它,它是 encoding arg 的默认值。请参阅bytes.decode
使用任何解码都会给我: AttributeError: 'str' object has no attribute 'decode'
答3:
HuntsBot周刊–不定时分享成功产品案例,学习他们如何成功建立自己的副业–huntsbot.com
这将一个字节列表连接成一个字符串:
>>> bytes_data = [112, 52, 52]
>>> "".join(map(chr, bytes_data))
'p44'
huntsbot.com汇聚了国内外优秀的初创产品创意,可按收入、分类等筛选,希望这些产品与实践经验能给您带来灵感。
谢谢,您的方法对我有用,而其他方法都没有。我有一个未编码的字节数组,需要将其转换为字符串。试图找到一种重新编码它的方法,以便我可以将它解码成一个字符串。这个方法非常有效!
@leetNightshade:但它的效率非常低。如果你有一个字节数组,你只需要解码。
@Martijn Pieters 我只是对这些其他答案做了一个简单的基准测试,运行了多次 10,000 次stackoverflow.com/a/3646405/353094,而上述解决方案实际上每次都快得多。在 Python 2.7.7 中运行 10,000 次需要 8 毫秒,而其他运行需要 12 毫秒和 18 毫秒。当然,根据输入、Python 版本等可能会有一些变化。对我来说似乎并不太慢。
@Sasszem:这种方法是一种变态的表达方式:a.decode('latin-1') where a = bytearray([112, 52, 52])("There Ain't No Such Thing as Plain Text"。如果您设法将字节转换为文本字符串,那么您使用了某种编码 - 在这种情况下为 latin-1)
对于 python 3,这应该等同于 bytes([112, 52, 52]) - btw bytes 对于局部变量来说是一个坏名字,因为它是 p3 内置的
答4:
打造属于自己的副业,开启自由职业之旅,从huntsbot.com开始!
如果您不知道编码,那么要以 Python 3 和 Python 2 兼容的方式将二进制输入读入字符串,请使用古老的 MS-DOS CP437 编码:
PY3K = sys.version_info >= (3, 0)
lines = []
for line in stream:
if not PY3K:
lines.append(line)
else:
lines.append(line.decode('cp437'))
因为编码是未知的,所以非英文符号会翻译成 cp437 的字符(英文字符不会被翻译,因为它们在大多数单字节编码和 UTF-8 中都匹配)。
将任意二进制输入解码为 UTF-8 是不安全的,因为您可能会得到以下信息:
>>> b'\x00\x01\xffsd'.decode('utf-8')
Traceback (most recent call last):
File "", line 1, in
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 2: invalid
start byte
这同样适用于 latin-1,它在 Python 2 中很流行(默认?)。请参阅 Codepage Layout 中的缺失点 - 这是 Python 与臭名昭著的 ordinal not in range 窒息的地方。
更新 20150604:有传言称 Python 3 具有 surrogateescape 错误策略,可以将内容编码为二进制数据而不会丢失数据和崩溃,但它需要转换测试,[binary] -> [str] -> [binary],以验证性能和可靠性。
更新 20170116:感谢 Nearoo 的评论 - 还有可能使用 backslashreplace 错误处理程序对所有未知字节进行斜线转义。这仅适用于 Python 3,因此即使使用此解决方法,您仍然会从不同的 Python 版本中获得不一致的输出:
PY3K = sys.version_info >= (3, 0)
lines = []
for line in stream:
if not PY3K:
lines.append(line)
else:
lines.append(line.decode('utf-8', 'backslashreplace'))
有关详细信息,请参阅 Python’s Unicode Support。
更新 20170119:我决定实现适用于 Python 2 和 Python 3 的斜线转义解码。它应该比 cp437 解决方案慢,但它应该在每个 Python 版本上产生相同的结果。
# --- preparation
import codecs
def slashescape(err):
""" codecs error handler. err is UnicodeDecode instance. return
a tuple with a replacement for the unencodable part of the input
and a position where encoding should continue"""
#print err, dir(err), err.start, err.end, err.object[:err.start]
thebyte = err.object[err.start:err.end]
repl = u'\\x'+hex(ord(thebyte))[2:]
return (repl, err.end)
codecs.register_error('slashescape', slashescape)
# --- processing
stream = [b'\x80abc']
lines = []
for line in stream:
lines.append(line.decode('utf-8', 'slashescape'))
我真的觉得 Python 应该提供一种机制来替换丢失的符号并继续。
@techtonik:这不适用于像在 python2 中那样的数组。
您也可以在 python 3 中使用 b'\x00\x01\xffsd'.decode('utf-8', 'ignore') 忽略 unicode 错误。
@anatolytechtonik 有可能将转义序列留在字符串中并继续:b'\x80abc'.decode("utf-8", "backslashreplace") 将导致 '\\x80abc'。此信息取自 unicode documentation page,自撰写此答案以来似乎已更新。
据我对 ISO 8859 系列的理解,ISO 定义仅定义可打印字符,而不是控制代码,这就是为什么您在维基百科的表格中看到空白的原因。然而在实践中,代码 0-31 和 127-159 被映射到相应的 unicode 控制代码。因此,使用 ISO-8859-1(又名 latin1)解码任意字节是安全的(这也适用于某些但并非所有其他 ISO-8859 系列编码)。
答5:
huntsbot.com提供全网独家一站式外包任务、远程工作、创意产品分享与订阅服务!
In Python 3,默认编码是”utf-8”,所以可以直接使用:
b'hello'.decode()
这相当于
b'hello'.decode(encoding="utf-8")
另一方面,in Python 2,编码默认为默认字符串编码。因此,您应该使用:
b'hello'.decode(encoding)
其中 encoding 是您想要的编码。
Note: 在 Python 2.7 中添加了对关键字参数的支持。
答6:
huntsbot.com – 高效赚钱,自由工作
我认为你实际上想要这个:
>>> from subprocess import *
>>> command_stdout = Popen(['ls', '-l'], stdout=PIPE).communicate()[0]
>>> command_text = command_stdout.decode(encoding='windows-1252')
Aaron 的回答是正确的,只是您需要知道要使用哪种编码。而且我相信 Windows 使用“windows-1252”。仅当您的内容中有一些不寻常的(非 ASCII)字符时才重要,但它会有所作为。
顺便说一句,它确实很重要的事实是 Python 转向对二进制和文本数据使用两种不同类型的原因:它不能在它们之间进行神奇的转换,因为除非你告诉它,否则它不知道编码!您知道的唯一方法是阅读 Windows 文档(或在此处阅读)。
open() 用于文本流的函数或 Popen() 如果您传递它 universal_newlines=True 会神奇地为您决定字符编码(Python 3.3+ 中的 locale.getpreferredencoding(False))。
'latin-1' 是设置了所有代码点的逐字编码,因此您可以使用它来有效地将字节字符串读入 Python 支持的任何类型的字符串(在 Python 2 上逐字读取,在 Python 3 上读取到 Unicode)。
@tripleee:'latin-1' 是获得 mojibake 的好方法。在 Windows 上也有神奇的替换:将数据从一个进程传送到另一个未修改的进程是非常困难的,例如 dir: \xb6 -> \x14 (the example at the end of my answer)
答7:
保持自己快人一步,享受全网独家提供的一站式外包任务、远程工作、创意产品订阅服务–huntsbot.com
由于这个问题实际上是在询问 subprocess 输出,因此您可以使用更直接的方法。最现代的方法是使用 subprocess.check_output 并传递 text=True (Python 3.7+) 以使用系统默认编码自动解码标准输出:
text = subprocess.check_output(["ls", "-l"], text=True)
对于 Python 3.6,Popen 接受 encoding 关键字:
>>> from subprocess import Popen, PIPE
>>> text = Popen(['ls', '-l'], stdout=PIPE, encoding='utf-8').communicate()[0]
>>> type(text)
str
>>> print(text)
total 0
-rw-r--r-- 1 wim badger 0 May 31 12:45 some_file.txt
如果您不处理子进程输出,则标题中问题的一般答案是将字节解码为文本:
>>> b'abcde'.decode()
'abcde'
如果没有参数,将使用 sys.getdefaultencoding()。如果您的数据不是 sys.getdefaultencoding(),那么您必须在 decode 调用中明确指定编码:
>>> b'caf\xe9'.decode('cp1250')
'café'
使用 utf-8 编码解码 ls 输出可能会失败(参见 my answer from 2016 中的示例)。
@Boris:如果给出 encoding 参数,则忽略 text 参数。
这是 subprocess 的正确答案。如果您只想等待子流程并获得其结果,也许仍然强调 Popen 几乎总是错误的工具;如文档所述,使用 subprocess.run 或旧功能 check_call 或 check_output 之一。
答8:
与HuntsBot一起,探索全球自由职业机会–huntsbot.com
将universal_newlines设置为True,即
command_stdout = Popen(['ls', '-l'], stdout=PIPE, universal_newlines=True).communicate()[0]
我一直在使用这种方法,并且有效。虽然,它只是根据系统上的用户偏好来猜测编码,所以它不像其他一些选项那么健壮。这就是它正在做的事情,引用 docs.python.org/3.4/library/subprocess.html:“如果 universal_newlines 为 True,则 [stdin、stdout 和 stderr] 将使用语言环境返回的编码以通用换行符模式作为文本流打开.getpreferredencoding(假)。”
On 3.7 您可以(并且应该)使用 text=True 而不是 universal_newlines=True。
答9:
一个优秀的自由职业者,应该有对需求敏感和精准需求捕获的能力,而huntsbot.com提供了这个机会
要将字节序列解释为文本,您必须知道相应的字符编码:
unicode_text = bytestring.decode(character_encoding)
例子:
>>> b'\xc2\xb5'.decode('utf-8')
'µ'
ls 命令可能会产生无法解释为文本的输出。 Unix 上的文件名可以是除斜杠 b’/’ 和零 b’\0’ 之外的任何字节序列:
>>> open(bytes(range(0x100)).translate(None, b'\0/'), 'w').close()
尝试使用 utf-8 编码解码这样的字节汤会引发 UnicodeDecodeError。
情况可能更糟。如果您使用错误的不兼容编码,解码可能会静默失败并产生 mojibake:
>>> '—'.encode('utf-8').decode('cp1252')
'—'
数据已损坏,但您的程序仍然不知道发生了故障。
一般来说,使用什么字符编码并不嵌入字节序列本身。您必须在带外传达此信息。某些结果比其他结果更有可能,因此存在可以猜测字符编码的 chardet 模块。一个 Python 脚本可能在不同的地方使用多个字符编码。
ls 输出可以使用 os.fsdecode() 函数转换为 Python 字符串,即使对于 undecodable filenames 也成功(它在 Unix 上使用 sys.getfilesystemencoding() 和 surrogateescape 错误处理程序):
import os
import subprocess
output = os.fsdecode(subprocess.check_output('ls'))
要获取原始字节,您可以使用 os.fsencode()。
如果您传递 universal_newlines=True 参数,则 subprocess 使用 locale.getpreferredencoding(False) 来解码字节,例如,它可以是 Windows 上的 cp1252。
要即时解码字节流,可以使用 io.TextIOWrapper():example。
不同的命令可能对其输出使用不同的字符编码,例如,dir 内部命令 (cmd) 可能使用 cp437。要解码其输出,您可以显式传递编码(Python 3.6+):
output = subprocess.check_output('dir', shell=True, encoding='cp437')
文件名可能与 os.listdir()(使用 Windows Unicode API)不同,例如,‘\xb6’ 可以替换为 ‘\x14’ — Python 的 cp437 编解码器映射 b’\x14’ 来控制字符 U+0014 而不是 U+00B6 (¶)。要支持具有任意 Unicode 字符的文件名,请参阅 Decode PowerShell output possibly containing non-ASCII Unicode characters into a Python string
答10:
保持自己快人一步,享受全网独家提供的一站式外包任务、远程工作、创意产品订阅服务–huntsbot.com
当 @Aaron Maenpaa’s answer 正常工作时,用户 recently asked:
还有更简单的方法吗? ‘fhand.read().decode(“ASCII”)’ […] 太长了!
您可以使用:
command_stdout.decode()
decode() 有一个 standard argument:
codecs.decode(obj, encoding=‘utf-8’, errors=‘strict’)
使用 'utf-8' 的 .decode() 可能会失败(命令的输出可能使用不同的字符编码,甚至返回不可解码的字节序列)。虽然如果输入是 ascii(utf-8 的子集),那么 .decode() 有效。
答11:
huntsbot.com洞察每一个产品背后的需求与收益,从而捕获灵感
如果您应该通过尝试 decode() 获得以下信息:
AttributeError:“str”对象没有属性“decode”
您还可以直接在强制转换中指定编码类型:
>>> my_byte_str
b'Hello World'
>>> str(my_byte_str, 'utf-8')
'Hello World'
原文链接:https://www.huntsbot.com/qa/Vdql/convert-bytes-to-a-string?lang=zh_CN&from=csdn
打造属于自己的副业,开启自由职业之旅,从huntsbot.com开始!
还没有评论,来说两句吧...