我将外部程序的标准输出捕获到 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
?
str(text_bytes)
不起作用?这对我来说似乎很奇怪。
str(text_bytes)
无法指定编码。根据 text_bytes 中的内容,text_bytes.decode('cp1250
)` 可能会导致与 text_bytes.decode('utf-8')
完全不同的字符串。
str
函数不再转换为真正的字符串。由于某种原因,我不得不明确地说出一种编码,我懒得通读原因。只需将其转换为 utf-8
并查看您的代码是否有效。例如var = var.decode('utf-8')
unicode_text = str(bytestring, character_encoding)
在 Python 3 上按预期工作。虽然 unicode_text = bytestring.decode(character_encoding)
更可取以避免与仅产生 bytes_obj
的文本表示而不是将其解码为文本的 str(bytes_obj)
混淆:str(b'\xb6', 'cp1252') == b'\xb6'.decode('cp1252') == '¶'
和 {6 }
Decode the bytes
object 产生一个字符串:
>>> b"abcde".decode("utf-8")
'abcde'
上面的示例假设 bytes
对象是 UTF-8,因为它是一种常见的编码。但是,您应该使用数据实际所在的编码!
解码字节字符串并将其转换为字符 (Unicode) 字符串。
蟒蛇 3:
encoding = 'utf-8'
b'hello'.decode(encoding)
或者
str(b'hello', encoding)
蟒蛇2:
encoding = 'utf-8'
'hello'.decode(encoding)
或者
unicode('hello', encoding)
variable = b'hello'
,那么 unicode_text = variable.decode(character_encoding)
variable = variable.decode()
自动将其转换为我想要的字符串格式。
encoding
arg 的默认值。请参阅bytes.decode
这将一个字节列表连接成一个字符串:
>>> bytes_data = [112, 52, 52]
>>> "".join(map(chr, bytes_data))
'p44'
a.decode('latin-1')
where a = bytearray([112, 52, 52])
("There Ain't No Such Thing as Plain Text"。如果您设法将字节转换为文本字符串,那么您使用了某种编码 - 在这种情况下为 latin-1
)
bytes([112, 52, 52])
- btw bytes 对于局部变量来说是一个坏名字,因为它是 p3 内置的
如果您不知道编码,那么要以 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 "<stdin>", line 1, in <module>
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'))
b'\x00\x01\xffsd'.decode('utf-8', 'ignore')
忽略 unicode 错误。
b'\x80abc'.decode("utf-8", "backslashreplace")
将导致 '\\x80abc'
。此信息取自 unicode documentation page,自撰写此答案以来似乎已更新。
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 中添加了对关键字参数的支持。
我认为你实际上想要这个:
>>> 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)。
'latin-1'
是获得 mojibake 的好方法。在 Windows 上也有神奇的替换:将数据从一个进程传送到另一个未修改的进程是非常困难的,例如 dir
: \xb6
-> \x14
(the example at the end of my answer)
由于这个问题实际上是在询问 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é'
encoding
参数,则忽略 text
参数。
subprocess
的正确答案。如果您只想等待子流程并获得其结果,也许仍然强调 Popen
几乎总是错误的工具;如文档所述,使用 subprocess.run
或旧功能 check_call
或 check_output
之一。
将universal_newlines设置为True,即
command_stdout = Popen(['ls', '-l'], stdout=PIPE, universal_newlines=True).communicate()[0]
要将字节序列解释为文本,您必须知道相应的字符编码:
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
当 @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()
有效。
如果您应该通过尝试 decode()
获得以下信息:
AttributeError:“str”对象没有属性“decode”
您还可以直接在强制转换中指定编码类型:
>>> my_byte_str
b'Hello World'
>>> str(my_byte_str, 'utf-8')
'Hello World'
如果您遇到此错误:
utf-8 codec can't decode byte 0x8a
,
那么最好使用以下代码将字节转换为字符串:
bytes = b"abcdefg"
string = bytes.decode("utf-8", "ignore")
我做了一个清理列表的功能
def cleanLists(self, lista):
lista = [x.strip() for x in lista]
lista = [x.replace('\n', '') for x in lista]
lista = [x.replace('\b', '') for x in lista]
lista = [x.encode('utf8') for x in lista]
lista = [x.decode('utf8') for x in lista]
return lista
.strip
、.replace
、.encode
等调用链接到一个列表推导中,并且只对列表进行一次迭代,而不是对它进行五次迭代。
处理来自 Windows 系统的数据(以 \r\n
行结尾)时,我的答案是
String = Bytes.decode("utf-8").replace("\r\n", "\n")
为什么?用多行 Input.txt 试试这个:
Bytes = open("Input.txt", "rb").read()
String = Bytes.decode("utf-8")
open("Output.txt", "w").write(String)
您的所有行尾都将加倍(到 \r\r\n
),从而导致额外的空行。 Python 的文本读取函数通常对行尾进行规范化,以便字符串仅使用 \n
。如果您从 Windows 系统接收二进制数据,Python 没有机会这样做。因此,
Bytes = open("Input.txt", "rb").read()
String = Bytes.decode("utf-8").replace("\r\n", "\n")
open("Output.txt", "w").write(String)
将复制您的原始文件。
.replace("\r\n", "\n")
加法。如果您想正确呈现 HTML,这就是答案。
对于 Python 3,这是一种从 byte
转换为 string
的更安全且 Pythonic 的方法:
def byte_to_str(bytes_or_str):
if isinstance(bytes_or_str, bytes): # Check if it's in bytes
print(bytes_or_str.decode('utf-8'))
else:
print("Object not of byte type")
byte_to_str(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')
输出:
total 0
-rw-rw-r-- 1 thomas thomas 0 Mar 3 07:03 file1
-rw-rw-r-- 1 thomas thomas 0 Mar 3 07:03 file2
byte_to_str
”,这意味着它将返回一个 str,但它只打印转换后的值,并且如果它失败(但不引发异常)。这种方法也是非 Python 的,并且混淆了您提供的 bytes.decode
解决方案。
对于“运行 shell 命令并将其输出作为文本而不是字节”的 特定 情况,在 Python 3.7 上,您应该使用 subprocess.run
并传入 text=True
(以及 {4 } 来捕获输出)
command_result = subprocess.run(["ls", "-l"], capture_output=True, text=True)
command_result.stdout # is a `str` containing your program's stdout
text
以前称为 universal_newlines
,并在 Python 3.7 中进行了更改(嗯,别名)。如果要支持 3.7 之前的 Python 版本,请传入 universal_newlines=True
而不是 text=True
来自 sys — System-specific parameters and functions:
要从/向标准流写入或读取二进制数据,请使用底层二进制缓冲区。例如,要将字节写入标准输出,请使用 sys.stdout.buffer.write(b'abc')
。
bytes
值中获取字符串值。
使用 .decode()
解码。这将解码字符串。传入 'utf-8'
) 作为内部的值。
字节
m=b'This is bytes'
转换为字符串
方法一
m.decode("utf-8")
或者
m.decode()
方法二
import codecs
codecs.decode(m,encoding="utf-8")
或者
import codecs
codecs.decode(m)
方法三
str(m,encoding="utf-8")
或者
str(m)[1:-1]
结果
'This is bytes'
如果要转换任何字节,而不仅仅是转换为字节的字符串:
with open("bytesfile", "rb") as infile:
str = base64.b85encode(imageFile.read())
with open("bytesfile", "rb") as infile:
str2 = json.dumps(list(infile.read()))
然而,这不是很有效。它将一张 2 MB 的图片变成 9 MB。
尝试这个
bytes.fromhex('c3a9').decode('utf-8')
我们可以使用 bytes.decode(encoding='utf-8', errors='strict')
解码字节对象以生成字符串作为文档。点击here
Python3
示例:
byte_value = b"abcde"
print("Initial value = {}".format(byte_value))
print("Initial value type = {}".format(type(byte_value)))
string_value = byte_value.decode("utf-8")
# utf-8 is used here because it is a very common encoding, but you need to use the encoding your data is actually in.
print("------------")
print("Converted value = {}".format(string_value))
print("Converted value type = {}".format(type(string_value)))
输出:
Initial value = b'abcde'
Initial value type = <class 'bytes'>
------------
Converted value = abcde
Converted value type = <class 'str'>
注意:在 Python3 中,默认编码类型是 utf-8
。所以,<byte_string>.decode("utf-8")
也可以写成 <byte_string>.decode()
尝试使用这个;此函数将忽略所有非字符集(如 utf-8
)二进制文件并返回一个干净的字符串。它已针对 python3.6
及更高版本进行了测试。
def bin2str(text, encoding = 'utf-8'):
"""Converts a binary to Unicode string by removing all non Unicode char
text: binary string to work on
encoding: output encoding *utf-8"""
return text.decode(encoding, 'ignore')
在这里,该函数将获取二进制文件并对其进行解码(使用 python 预定义字符集将二进制数据转换为字符,并且 ignore
参数忽略二进制文件中的所有非字符集数据,最后返回您想要的 string
值。
如果您不确定编码,请使用 sys.getdefaultencoding()
获取设备的默认编码。
"windows-1252"
也不可靠(例如,对于其他语言版本的 Windows),使用sys.stdout.encoding
不是最好吗?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