将字节转换为字符串

问:

我将外部程序的标准输出捕获到 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开始!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值