将字节转换为string?
我正在使用这个代码从外部程序获得标准输出:
>>> from subprocess import * >>> command_stdout = Popen(['ls', '-l'], stdout=PIPE).communicate()[0]
communications()方法返回一个字节数组:
>>> 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'
不过,我想用普通的Pythonstring来处理输出。 所以我可以这样打印:
>>> 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'
有谁知道如何将字节值转换回string? 我的意思是,使用“电池”,而不是手动进行。 我希望Python 3能够正常运行。
你需要解码bytes对象来产生一个string:
>>> 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'
我觉得这样很容易:
bytes = [112, 52, 52] "".join(map(chr, bytes)) >> p44
您需要解码字节string并将其转换为字符(unicode)string。
b'hello'.decode(encoding)
要么
str(b'hello', encoding)
如果你不知道编码,那么要用Python 3和Python 2兼容的方式把二进制input读成string,用古老的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中匹配)。
解码任意二进制input到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
对于Python 2来说, latin-1
也是一样(缺省是?)。请参阅代码页布局中的缺失点 – 这是Python扼杀臭名昭着的ordinal not in range
。
更新20150604 :有传闻说,Python 3具有surrogateescape
错误策略编码的东西到二进制数据没有数据丢失和崩溃,但它需要转换testing[binary] -> [str] -> [binary]
来validation性能和可靠性。
UPDATE 20170116 :感谢Nearoo的评论 – 还有一种可能性,用backslashreplace
error handling程序来跳过所有未知字节的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
解决scheme慢,但是它应该在每个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'))
我想你真正想要的是这样的:
>>> from subprocess import * >>> command_stdout = Popen(['ls', '-l'], stdout=PIPE).communicate()[0] >>> command_text = command_stdout.decode(encoding='windows-1252')
亚伦的回答是正确的,除了你需要知道使用哪种编码。 我相信Windows使用“windows-1252”。 只有在你的内容中有一些不寻常的(非ascii)字符才会有影响,但是这样做会有所帮助。
顺便说一句,它的重要性在于Python转向使用两种不同types的二进制和文本数据的原因:它不能在它们之间神奇地转换,因为它不知道编码,除非你告诉它! 你会知道的唯一方法是阅读Windows文档(或在这里阅读)。
将universal_newlines设置为True,即
command_stdout = Popen(['ls', '-l'], stdout=PIPE, universal_newlines=True).communicate()[0]
在Python 3中,您可以直接使用:
b'hello'.decode()
相当于
b'hello'.decode(encoding="utf-8")
这里的默认编码是“utf-8”,或者你可以通过以下方式检查:
>> import sys >> sys.getdefaultencoding()
虽然@Aaron Maenpaa的答案正确,但最近一位用户问道
还有更简单的方法吗? 'fhand.read()。decode(“ASCII”)'[…]这么长!
您可以使用
command_stdout.decode()
decode()
有一个标准的参数
codecs.decode(obj, encoding='utf-8', errors='strict')
要将字节序列解释为文本,您必须知道相应的字符编码:
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') '—'
数据已损坏,但您的程序仍不知道发生了故障。
一般来说,字符序列本身并不embedded使用什么字符编码。 你必须在带外传达这个信息。 一些结果比其他结果更可能存在,因此chardet
模块可以猜测字符编码。 一个Python脚本可能在不同的地方使用多个字符编码。
使用os.fsdecode()
函数可以将ls
输出转换为Pythonstring,即使对于不可译码的文件名 (在Unix上它使用sys.getfilesystemencoding()
和surrogateescape
error handling程序)
import os import subprocess output = os.fsdecode(subprocess.check_output('ls'))
为了获得原始字节,你可以使用os.fsencode()
。
如果传递的是universal_newlines=True
参数,则subprocess
locale.getpreferredencoding(False)
使用locale.getpreferredencoding(False)
来解码字节,例如,它可以是Windows上的cp1252
。
要实时解码字节stream,可以使用io.TextIOWrapper()
: 例如 。
不同的命令可能使用不同的字符编码输出,例如, dir
internal command( 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字符的poweshell输出解码为pythonstring
我做了一个清理列表的函数
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
对于Python 3,这是一个更安全的Pythonic方法从byte
转换为string
:
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\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
我做了一个清理列表的函数
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
从http://docs.python.org/3/library/sys.html ,
要向标准stream写入或读取二进制数据,请使用基础二进制缓冲区。 例如,要将字节写入标准输出,请使用sys.stdout.buffer.write(b'abc')。