it-swarm.asia

将字节转换为字符串?

我正在使用此代码从外部程序获取标准输出:

>>> 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。

1642
Tomas Sedovic

您需要解码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'
2721
Aaron Maenpaa

我觉得这很简单:

bytes = [112, 52, 52]
"".join(map(chr, bytes))
>> p44
143
Sisso

您需要解码字节字符串并将其转换为字符(unicode)字符串。

b'hello'.decode(encoding)

或者在Python 3上

str(b'hello', encoding)
129
dF.

如果您不知道编码,那么要以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]来验证性能和可靠性。

UPDATE 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

UPDATE 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'))
67
anatoly techtonik

在Python 3 中,默认编码是"utf-8",因此您可以直接使用:

b'hello'.decode()

这相当于

b'hello'.decode(encoding="utf-8")

另一方面, 在Python 2中 ,编码默认为默认的字符串编码。因此,你应该使用:

b'hello'.decode(encoding)

其中encoding是您想要的编码。

注意: 在Python 2.7中添加了对关键字参数的支持。

61
lmiguelvargasf

我想你真正想要的是:

>>> 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文档(或在此处阅读)。

36
mcherm

将universal_newlines设置为True,即.

command_stdout = Popen(['ls', '-l'], stdout=PIPE, universal_newlines=True).communicate()[0]
29
ContextSwitch

虽然 @Aaron Maenpaa的回答 只是有效,一个用户 最近问

还有更简单的方法吗? 'fhand.read()。decode(“ASCII”)'[...]它太长了!

您可以使用

command_stdout.decode()

decode()有一个 标准参数

codecs.decode(obj, encoding='utf-8', errors='strict')

17
serv-inc

要将字节序列解释为文本,您必须知道相应的字符编码:

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模块,可以 guess 字符编码。单个Python脚本可能在不同的位置使用多个字符编码。


使用os.fsdecode()函数可以将ls输出转换为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)来解码字节,例如,它可以是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字符的文件名,请参阅 将可能包含非ascii unicode字符的poweshell输出解码为python字符串

13
jfs

由于这个问题实际上是在询问subprocess输出,所以你有一个更直接的方法,因为Popen接受一个 encoding keyword(在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

其他用户的一般答案是 decode bytes to text:

>>> b'abcde'.decode()
'abcde'

没有参数,将使用 sys.getdefaultencoding() 。如果您的数据不是sys.getdefaultencoding(),那么您必须在 decode call中明确指定编码:

>>> b'caf\xe9'.decode('cp1250')
'café'
12
wim

如果你应该通过尝试decode()来获得以下内容:

AttributeError: 'str' object has no attribute 'decode'

您还可以直接在强制转换中指定编码类型:

>>> my_byte_str
b'Hello World'

>>> str(my_byte_str, 'utf-8')
'Hello World'
6
Broper

我做了一个清理列表的功能

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
4
eafloresf

处理来自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)

将复制您的原始文件。

4
bers

对于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
2
Inconnu
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))
1
Leonardo Filipe

如果要转换任何字节,而不仅仅是转换为字节的字符串:

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。

0
HCLivess