将字节转换为字符串?

Tom*_*vic 1968 python string python-3.x

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

>>> from subprocess import *
>>> command_stdout = Popen(['ls', '-l'], stdout=PIPE).communicate()[0]
Run Code Online (Sandbox Code Playgroud)

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'
Run Code Online (Sandbox Code Playgroud)

但是,我想将输出作为普通的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
Run Code Online (Sandbox Code Playgroud)

我认为这是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'
Run Code Online (Sandbox Code Playgroud)

有人知道如何将字节值转换回字符串吗?我的意思是,使用"电池"而不是手动操作.而且我希望它能用于Python 3.

Aar*_*paa 3195

您需要解码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'
Run Code Online (Sandbox Code Playgroud)

  • 使用"windows-1252"`也不可靠(例如,对于Windows的其他语言版本),最好不要使用`sys.stdout.encoding`? (55认同)
  • 在Python 2.7.6中不处理`b"\ x80\x02\x03".decode("utf-8")` - >`UnicodeDecodeError:'utf8'编解码器无法解码位置0的字节0x80:无效启动byte`. (41认同)
  • 也许这会对某些人有所帮助:有时你使用字节数组进行TCP通信.如果要将字节数组转换为字符串,请删除尾随的'\ x00'字符,以下答案是不够的.使用b'example\x00\x00'.decode('utf-8').strip('\ x00')然后. (11认同)
  • 如果内容是随机二进制值,则"utf-8"转换可能会失败.请参阅@techtonik答案(如下)http://stackoverflow.com/a/27527728/198536 (7认同)
  • 我已经在http://bugs.python.org/issue17860上填写了有关记录此文档的错误-随时提出补丁。如果很难做出贡献,欢迎评论如何改进。 (2认同)
  • 虽然这通常是要走的路,但您需要确保编码正确,否则您的代码最终可能会呕吐。更糟糕的是,来自外部世界的数据可能包含意外的编码。https://pypi.org/project/chardet/ 上的 chardet 库可以帮助你解决这个问题,但同样,总是防御性编程,有时甚至 chardet 也会出错,所以用一些适当的异常处理来包装你的垃圾。 (2认同)
  • 为什么`str(text_bytes)`不起作用?这对我来说似乎很奇怪。 (2认同)

dF.*_*dF. 182

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

encoding = 'utf-8'
'hello'.decode(encoding)
Run Code Online (Sandbox Code Playgroud)

或者在Python 3上

unicode('hello', encoding)
Run Code Online (Sandbox Code Playgroud)

  • 在 Python 3 上,如果字符串位于变量中怎么办? (3认同)
  • 对我来说,“variable = variable.decode()”会自动将其转换为我想要的字符串格式。 (3认同)
  • @AlexHall> fwiw,您可能有兴趣知道 automagic 使用 utf8,这是“encoding”参数的默认值(如果您不提供它)。请参阅[`bytes.decode`](https://docs.python.org/3/library/stdtypes.html#bytes.decode) (2认同)

Sis*_*sso 164

我觉得这很简单:

bytes_data = [112, 52, 52]
"".join(map(chr, bytes_data))
>> p44
Run Code Online (Sandbox Code Playgroud)

  • @Martijn Pieters我刚刚用这些其他答案做了一个简单的基准测试,运行多次10,000次运行http://stackoverflow.com/a/3646405/353094而且上述解决方案实际上每次都要快得多.对于Python 2.7.7中的10,000次运行,需要8ms,而其他运行时间为12ms和18ms.当然,根据输入,Python版本等可能存在一些变化.对我来说似乎不太慢. (11认同)
  • 谢谢,你的方法对我来说很有效.我有一个非编码的字节数组,我需要变成一个字符串.试图找到一种方法来重新编码它,以便我可以将其解码为字符串.这种方法效果很好! (6认同)
  • @leetNightshade:但效率非常低.如果你有一个字节数组,你只需要解码. (5认同)
  • @Martijn Pieters是的.因此,就这一点而言,这不是问题正文的最佳答案.标题是误导,不是吗?他/她想将字节字符串转换为常规字符串,而不是将字节数组转换为字符串.这个答案适用于所提问题的标题. (5认同)
  • 对于python 3,这应该相当于[`bytes([112,52,52])`](/sf/answers/2465378681/) - btw字节对于局部变量来说是一个坏名称,因为这是一个内置的p3 (5认同)
  • @Sasszem:此方法是一种变态的表达方式:`a.decode('latin-1')`其中`a = bytearray([112,52,52])`((“”没有这样的东西纯文本”](http://www.joelonsoftware.com/articles/Unicode.html。如果您设法将字节转换为文本字符串,则可以使用某种编码-在这种情况下为“ latin-1”) (2认同)
  • @leetNightshade:为了完整起见:`bytes(list_of_integers).decode('ascii')` 比 Python 3.6 上的 `''.join(map(chr, list_of_integers))` 快大约 1/3。 (2认同)

ana*_*nik 80

如果您不知道编码,那么要以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'))
Run Code Online (Sandbox Code Playgroud)

因为编码是未知的,所以期望非英语符号转换为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
Run Code Online (Sandbox Code Playgroud)

这同样适用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'))
Run Code Online (Sandbox Code Playgroud)

有关详细信息,请参阅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'))
Run Code Online (Sandbox Code Playgroud)

  • 我真的觉得Python应该提供一种机制来替换丢失的符号并继续. (6认同)
  • @anatolytechtonik有可能将转义序列留在字符串中继续前进:`b'\ x80abc'.decode("utf-8","backslashreplace")`将导致''\\ x80abc'.此信息来自[unicode文档页面](https://docs.python.org/3/howto/unicode.html#python-s-unicode-support),该答案自编写此答案后似乎已更新. (3认同)
  • 辉煌!对于256 MB的文件,这比@Sisso的方法快得多! (2认同)
  • 你也可以在 python 3 中使用 `b'\x00\x01\xffsd'.decode('utf-8', 'ignore')` 忽略 unicode 错误。 (2认同)
  • 这个答案是不正确的。latin-1,即 ISO-8859-1 编码完全能够处理任意二进制数据 - `bytes(range(256)).decode('latin-1')` 在现代 Python 版本上运行没有错误,我可以想不出它会失败的原因。Latin-1 的*重点*在于它将每个字节映射到 Unicode 中的前 256 个代码点 - 或者更确切地说,自 1991 年第一个版本以来就选择了 Unicode 的顺序,因此前 256 个代码点将匹配 Latin-1。**打印**字符串时可能会遇到问题,但这完全是正交的。 (2认同)

lmi*_*asf 75

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

b'hello'.decode()
Run Code Online (Sandbox Code Playgroud)

这相当于

b'hello'.decode(encoding="utf-8")
Run Code Online (Sandbox Code Playgroud)

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

b'hello'.decode(encoding)
Run Code Online (Sandbox Code Playgroud)

encoding你想要的编码在哪里.

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


mch*_*erm 38

我想你真正想要的是:

>>> from subprocess import *
>>> command_stdout = Popen(['ls', '-l'], stdout=PIPE).communicate()[0]
>>> command_text = command_stdout.decode(encoding='windows-1252')
Run Code Online (Sandbox Code Playgroud)

Aaron的回答是正确的,除了你需要知道要使用的WHICH编码.我相信Windows使用'windows-1252'.只有你的内容中有一些不寻常的(非ascii)字符才有意义,但它会产生影响.

顺便说一句,事实上它是重要的是Python转向使用两种不同类型的二进制和文本数据:它不能神奇地在它们之间转换,因为除非你告诉它,它不知道编码!您将知道的唯一方法是阅读Windows文档(或在此处阅读).

  • `open()`函数用于文本流或`Popen()`如果你传递它`universal_newlines = True`做魔法决定你的字符编码(Python 3.3+中的`locale.getpreferredencoding(False)`). (3认同)
  • “'latin-1''是逐字编码,设置了所有代码点,因此您可以使用它来将字节字符串有效地读入Python支持的任何类型的字符串(因此,在Python 2上逐字转换为在Python 3中为Unicode)。 (2认同)

小智 29

将universal_newlines设置为True,即

command_stdout = Popen(['ls', '-l'], stdout=PIPE, universal_newlines=True).communicate()[0]
Run Code Online (Sandbox Code Playgroud)

  • 我一直在使用这种方法,它的工作原理.虽然,它只是根据您系统上的用户偏好来猜测编码,因此它不像其他一些选项那样强大.这就是它正在做的,参考docs.python.org/3.4/library/subprocess.html:"如果universal_newlines为True,[stdin,stdout和stderr]将使用locale返回的编码以通用换行模式打开文本流.getpreferredencoding(假)". (5认同)
  • [在 3.7 上](https://docs.python.org/3/whatsnew/3.7.html#subprocess) 你可以(也应该)做 `text=True` 而不是 `universal_newlines=True`。 (2认同)

Yas*_*r M 21

如果您遇到此错误:

utf-8编解码器无法解码字节0x8a,

那么最好使用以下代码将字节转换为字符串:

bytes = b"abcdefg"
string = bytes.decode("utf-8", "ignore") 
Run Code Online (Sandbox Code Playgroud)


Sup*_*mer 20

字节

m=b'This is bytes'
Run Code Online (Sandbox Code Playgroud)

转换为字符串

方法一

m.decode("utf-8")
Run Code Online (Sandbox Code Playgroud)

或者

m.decode()
Run Code Online (Sandbox Code Playgroud)

方法二

import codecs
codecs.decode(m,encoding="utf-8")
Run Code Online (Sandbox Code Playgroud)

或者

import codecs
codecs.decode(m)
Run Code Online (Sandbox Code Playgroud)

方法三

str(m,encoding="utf-8")
Run Code Online (Sandbox Code Playgroud)

或者

str(m)[2:-1]
Run Code Online (Sandbox Code Playgroud)

结果

'This is bytes'
Run Code Online (Sandbox Code Playgroud)


ser*_*inc 19

虽然@Aaron Maenpaa的答案正常,但是一位用户最近问道

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

您可以使用

command_stdout.decode()
Run Code Online (Sandbox Code Playgroud)

decode()有一个标准的论点

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


Shu*_*pta 17

我们可以使用 解码 bytes 对象以生成字符串bytes.decode(encoding='utf-8', errors='strict')。有关文档,请参阅bytes.decode

Python 3 示例:

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)))
Run Code Online (Sandbox Code Playgroud)

输出:

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)))
Run Code Online (Sandbox Code Playgroud)

注意:在Python 3中,默认编码类型为UTF-8。所以,<byte_string>.decode("utf-8")也可以写成<byte_string>.decode()


wim*_*wim 15

由于这个问题实际上是在询问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
Run Code Online (Sandbox Code Playgroud)

其他用户的一般答案是将字节解码为文本:

>>> b'abcde'.decode()
'abcde'
Run Code Online (Sandbox Code Playgroud)

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

>>> b'caf\xe9'.decode('cp1250')
'café'
Run Code Online (Sandbox Code Playgroud)

  • 或使用Python 3.7,您可以通过[`text = True`](https://docs.python.org/3/library/subprocess.html#subprocess.Popen)使用给定的编码来解码stdin,stdout和stderr(如果设置),否则系统默认设置。Popen(['ls','-l'],stdout = PIPE,text = True) (3认同)

jfs*_*jfs 14

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

unicode_text = bytestring.decode(character_encoding)
Run Code Online (Sandbox Code Playgroud)

例:

>>> b'\xc2\xb5'.decode('utf-8')
'µ'
Run Code Online (Sandbox Code Playgroud)

ls命令可能会产生无法解释为文本的输出.Unix上的文件名可以是除斜杠b'/'和零 之外的任何字节序列b'\0':

>>> open(bytes(range(0x100)).translate(None, b'\0/'), 'w').close()
Run Code Online (Sandbox Code Playgroud)

尝试使用utf-8编码解码这样的字节汤UnicodeDecodeError.

可能会更糟. 如果使用错误的不兼容编码,解码可能会无声地失败并产生mojibake:

>>> '—'.encode('utf-8').decode('cp1252')
'—'
Run Code Online (Sandbox Code Playgroud)

数据已损坏,但您的程序仍未发现故障已发生.

通常,要使用的字符编码不嵌入字节序列本身.您必须在带外传达此信息.某些结果比其他结果更可能,因此chardet存在可以猜测字符编码的模块.单个Python脚本可能在不同的位置使用多个字符编码.


ls输出可以使用os.fsdecode() 即使对于不可解码的文件名也成功的函数转换为Python字符串(它在Unix上使用 sys.getfilesystemencoding()surrogateescape错误处理程序):

import os
import subprocess

output = os.fsdecode(subprocess.check_output('ls'))
Run Code Online (Sandbox Code Playgroud)

要获得原始字节,您可以使用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')
Run Code Online (Sandbox Code Playgroud)

文件名可能与os.listdir()(使用Windows Unicode API)不同,例如,'\xb6'可以用'\x14'-Python的cp437编解码器映射b'\x14'代替控制字符U + 0014而不是U + 00B6().要支持具有任意Unicode字符的文件名,请参阅将 可能包含非ascii unicode字符的powehell输出解码为python字符串


Bro*_*per 9

如果您通过尝试获得以下内容decode():

decode()

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

>>> my_byte_str
b'Hello World'

>>> str(my_byte_str, 'utf-8')
'Hello World'
Run Code Online (Sandbox Code Playgroud)


eaf*_*esf 8

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

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
Run Code Online (Sandbox Code Playgroud)

  • 实际上,您可以在一个列表推导式中链接所有`.strip`、`.replace`、`.encode` 等调用,并且只迭代列表一次而不是迭代五次。 (6认同)

Inc*_*nnu 8

对于 Python 3,这是一种更安全和Pythonic 的从 转换byte为 的方法string

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')
Run Code Online (Sandbox Code Playgroud)

输出:

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
Run Code Online (Sandbox Code Playgroud)

  • 1)正如@bodangly 所说,类型检查根本不是pythonic。2)您编写的函数名为“`byte_to_str`”,这意味着它将返回一个str,但它只打印转换后的值,*并且*如果失败则打印错误消息(但不会引发异常)。这种方法也是 unpythonic 并混淆了您提供的 `bytes.decode` 解决方案。 (6认同)

ber*_*ers 5

当使用Windows系统中的数据(以\r\n行结尾)时,我的答案是

String = Bytes.decode("utf-8").replace("\r\n", "\n")
Run Code Online (Sandbox Code Playgroud)

为什么?尝试使用多行Input.txt:

Bytes = open("Input.txt", "rb").read()
String = Bytes.decode("utf-8")
open("Output.txt", "w").write(String)
Run Code Online (Sandbox Code Playgroud)

您所有的行尾都将加倍(到\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)
Run Code Online (Sandbox Code Playgroud)

将复制您的原始文件。


小智 5

对于“运行 shell 命令并以文本而不是字节形式获取其输出”的特定subprocess.run情况,在 Python 3.7 上,您应该使用并传入text=True(以及capture_output=True捕获输出)

command_result = subprocess.run(["ls", "-l"], capture_output=True, text=True)
command_result.stdout  # is a `str` containing your program's stdout
Run Code Online (Sandbox Code Playgroud)

text曾经被称为universal_newlines,并且在 Python 3.7 中被更改(好吧,别名)。如果要支持3.7之前的Python版本,则传入universal_newlines=True而不是text=True