我正在使用此代码从外部程序获取标准输出:
>>> from subprocess import * >>> command_stdout = Popen(['ls', '-l'], stdout=PIPE).communicate()[0]
communic()方法返回一个字节数组:
>>> 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()方法的用途,但是当我尝试它时,我又得到了相同的字节数组:
>>> 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对象以生成字符串:
>>> 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'
您需要解码字节字符串并将其转换为字符(unicode)字符串.
encoding = 'utf-8' 'hello'.decode(encoding)
或者在Python 3上
unicode('hello', encoding)
我觉得这很简单:
bytes_data = [112, 52, 52] "".join(map(chr, bytes_data)) >> p44
如果您不知道编码,那么要以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的流行(默认?).请参阅代码页布局中的缺失点- 这是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'))
有关详细信息,请参阅https://docs.python.org/3/howto/unicode.html#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 3中,默认编码是"utf-8"
,因此您可以直接使用:
b'hello'.decode()
这相当于
b'hello'.decode(encoding="utf-8")
另一方面,在Python 2中,编码默认为默认字符串编码.因此,你应该使用:
b'hello'.decode(encoding)
encoding
你想要的编码在哪里.
注意: Python 2.7中添加了对关键字参数的支持.
我想你真正想要的是:
>>> from subprocess import * >>> command_stdout = Popen(['ls', '-l'], stdout=PIPE).communicate()[0] >>> command_text = command_stdout.decode(encoding='windows-1252')
Aaron的回答是正确的,除了你需要知道要使用的WHICH编码.我相信Windows使用'windows-1252'.只有你的内容中有一些不寻常的(非ascii)字符才有意义,但它会产生影响.
顺便说一句,事实上它是重要的是Python转向使用两种不同类型的二进制和文本数据:它不能神奇地在它们之间转换,因为除非你告诉它,它不知道编码!您将知道的唯一方法是阅读Windows文档(或在此处阅读).
将universal_newlines设置为True,即
command_stdout = Popen(['ls', '-l'], stdout=PIPE, universal_newlines=True).communicate()[0]
虽然@Aaron Maenpaa的答案正常,但是一位用户最近问道
还有更简单的方法吗?'fhand.read().decode("ASCII")'[...]它太长了!
您可以使用
command_stdout.decode()
decode()
有一个标准的论点
codecs.decode(obj, encoding='utf-8', errors='strict')
由于这个问题实际上是在询问subprocess
输出,因此您可以使用更直接的方法,因为它Popen
接受了一个编码关键字(在Python 3.6+中):
>>> 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é'
要将字节序列解释为文本,您必须知道相应的字符编码:
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字符串(它在Unix上使用
sys.getfilesystemencoding()
和surrogateescape
错误处理程序):
import os import subprocess output = os.fsdecode(subprocess.check_output('ls'))
要获得原始字节,您可以使用os.fsencode()
.
如果您传递universal_newlines=True
参数然后subprocess
用于
locale.getpreferredencoding(False)
解码字节,例如,它可以
cp1252
在Windows上.
要动态解码字节流,
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字符的文件名,请参阅将 可能包含非ascii unicode字符的powehell输出解码为python字符串
如果您通过尝试获得以下内容decode()
:
decode()
您还可以直接在强制转换中指定编码类型:
>>> my_byte_str b'Hello World' >>> str(my_byte_str, 'utf-8') 'Hello World'
当使用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)
将复制您的原始文件。