我用这段代码从外部程序得到标准输出:
1 2 | >>> from subprocess import * >>> command_stdout = Popen(['ls', '-l'], stdout=PIPE).communicate()[0] |
方法返回一个字节数组:
1 2 3 4 5 | >>> command_stdout b'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 ' |
但是,我希望将输出作为普通的Python字符串处理。所以我可以这样打印出来:
1 2 3 | >>> 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()方法的作用,但是当我尝试它时,我又得到了相同的字节数组:
1 2 3 4 5 | >>> binascii.b2a_qp(command_stdout) b'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 ' |
有人知道如何将字节值转换回字符串吗?我的意思是,使用"电池"而不是手动操作。我希望Python 3没问题。
你需要解码字节对象产生一个字符串:
1 2 3 4 5 6 7 | >>> b"abcde" b'abcde' # utf-8 is used here because it is a very common encoding, but you # need to use the encoding your data is actually in. >>> b"abcde".decode("utf-8") 'abcde' |
我认为这个方法很简单:
1 2 3 | bytes = [112, 52, 52] "".join(map(chr, bytes)) >> p44 |
您需要解码字节字符串并将其转换为字符(unicode)字符串。
1 | b'hello'.decode(encoding) |
或者在python3上
1 | str(b'hello', encoding) |
如果不知道编码,那么要将二进制输入读入字符串中,在python3和python2兼容的方式下,使用古老的MS-DOS cp437编码:
1 2 3 4 5 6 7 8 | PY3K = sys.version_info >= (3, 0) lines = [] for line in stream: if not PY3K: lines.append(line) else: lines.append(line.decode('cp437')) |
因为编码是未知的,所以期望非英语符号转换为
解码任意二进制输入到UTF-8是不安全的,因为你可能会得到:
1 2 3 4 5 | >>> 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 |
同样的情况也适用于
更新20150604:有传言说Python 3有
更新20170116:感谢评论近oo -也有可能斜杠转义所有未知字节与
1 2 3 4 5 6 7 8 | 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')) |
有关详细信息,请参见https://docs.python.org/3/howto/unicode.html# pythons-unicode -support。
更新20170119:我决定实现斜杠转义解码,适用于Python 2和Python 3。它应该比
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | # --- 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')) |
在python3中,默认编码是
1 | b'hello'.decode() |
相当于
1 | b'hello'.decode(encoding="utf-8") |
另一方面,在python2中,编码默认为默认字符串编码。因此,你应该使用:
1 | b'hello'.decode(encoding) |
其中
注意:Python 2.7中添加了对关键字参数的支持。
我认为你真正想要的是:
1 2 3 | >>> 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文档(或在这里阅读)。
将universal_newlines设置为True,即
1 | command_stdout = Popen(['ls', '-l'], stdout=PIPE, universal_newlines=True).communicate()[0] |
虽然@Aaron Maenpaa的回答是正确的,但最近一位用户问道
Is there any more simply way? 'fhand.read().decode("ASCII")' [...] It's so long!
您可以使用
1 | command_stdout.decode() |
codecs.decode(obj, encoding='utf-8', errors='strict')
要将字节序列解释为文本,必须知道相应的字符编码:
1 | unicode_text = bytestring.decode(character_encoding) |
例子:
1 2 | >>> b'\xc2\xb5'.decode('utf-8') 'μ' |
1 | >>> open(bytes(range(0x100)).translate(None, b'\0/'), 'w').close() |
尝试使用utf-8编码解码这样的字节汤会引发
情况可能更糟。解码可能会无声地失败,并产生mojibake如果使用错误的不兼容编码:
1 2 | >>> '—'.encode('utf-8').decode('cp1252') 'a€"' |
数据已损坏,但您的程序仍然不知道发生了故障发生。
通常,要使用的字符编码并不嵌入到字节序列本身。你必须在乐队外传达这个信息。有些结果比其他结果更有可能,因此存在
1 2 3 4 | import os import subprocess output = os.fsdecode(subprocess.check_output('ls')) |
要获得原始字节,可以使用
如果传递
要实时解码字节流,
不同的命令可能使用不同的字符编码输出例如,
1 | output = subprocess.check_output('dir', shell=True, encoding='cp437') |
文件名可能与
因为这个问题实际上是关于
1 2 3 4 5 6 7 | >>> 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 |
对于其他用户,通常的解决方案是将字节解码为文本:
1 2 | >>> b'abcde'.decode() 'abcde' |
在没有参数的情况下,将使用
1 2 | >>> b'caf\xe9'.decode('cp1250') 'café' |
如果您应该得到以下通过尝试
您还可以在一个cast中直接指定编码类型:
1 2 3 4 5 | >>> my_byte_str b'Hello World' >>> str(my_byte_str, 'utf-8') 'Hello World' |
当处理来自Windows系统的数据(以
行结尾)时,我的答案是
1 2 3 4 | String = Bytes.decode("utf-8").replace(" "," ") |
为什么?用多行输入试试。txt:
1 2 3 | Bytes = open("Input.txt","rb").read() String = Bytes.decode("utf-8") open("Output.txt","w").write(String) |
所有的行结束符都将加倍(到
),导致额外的空行。Python的文本读取函数通常对行尾进行规范化,以便字符串只使用
1 2 3 4 5 6 | Bytes = open("Input.txt","rb").read() String = Bytes.decode("utf-8").replace(" "," ") open("Output.txt","w").write(String) |
将复制原始文件。
我创建了一个函数来清理列表
1 2 3 4 5 6 7 8 9 | def cleanLists(self, lista): lista = [x.strip() for x in lista] lista = [x.replace(' ', '') 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 |
对于python3来说,将
1 2 3 4 5 6 7 8 9 10 | def byte_to_str(bytes_or_str): if isinstance(bytes_or_str, bytes): #check if its in bytes print(bytes_or_str.decode('utf-8')) else: print("Object not of byte type") byte_to_str(b'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 ') |
输出:
1 2 3 | 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 |
1 2 3 4 5 6 7 8 9 10 | def toString(string): try: return v.decode("utf-8") except ValueError: return string b = b'97.080.500' s = '97.080.500' print(toString(b)) print(toString(s)) |
从http://docs.python.org/3/library/sys.html,
要从标准流中写入或读取二进制数据,请使用底层二进制缓冲区。例如,要将字节写入stdout,可以使用sys.stdout.buffer.write(b'abc')。