这是我的代码:
#! /usr/bin/env python3
import subprocess
a = subprocess.check_output('echo -n "hello world!"',shell=True)
print("a="+str(a))
输出:
a=b'hello world!'
如果我在universal_newlines=True
的调用中包含参数check_output
,那么我会得到所需的输出:
a=hello world!
为了更好地理解现代(Unicode)时代的文本编程的神秘世界,我想知道如何在不指定universal_newlines=True
的情况下生成第二个输出。换句话说,我调用什么函数来转换a
,以便它将产生所需的输出。
一个工作的例子会有很长的路要走。详细的解释很好,但是对于没有经验的人来说它们往往有点混乱 - 可能是因为使用了重载的术语,可能是因为Python2和Python3之间的差异,或者可能只是因为我很少需要考虑文本编码我的工作 - 我工作的大部分工具都不需要像这样的特殊处理。
另外:我认为第一个输出是bytes
类型,但第二个输出的类型是什么?我的猜测是使用UTF-8编码str
。
答案 0 :(得分:2)
正如Ignacio的评论最初暗示的那样,您可以使用decode
:
>>> a = b"hello world!"
>>> print("a="+str(a))
a=b'hello world!'
>>> print("a="+a.decode())
a=hello world!
答案 1 :(得分:1)
来自subprocess.check_output()
docs:
默认情况下,此函数将以编码字节的形式返回数据。该 输出数据的实际编码可能取决于命令 调用,所以解码到文本往往需要在处理 应用水平。
通过将
universal_newlines
设置为True
,可以覆盖此行为 如下文Frequently Used Arguments中所述。
如果您点击Frequently Used Arguments的链接;它描述了universal_newlines=True
的作用:
如果
universal_newlines
为False
,则文件对象为stdin,stdout和 stderr将作为二进制流打开,并且没有行结束转换 完了。如果
universal_newlines
为True
,则这些文件对象将被打开为 使用返回的编码在通用换行模式下文本流locale.getpreferredencoding(False)
。对于stdin,行结束字符 输入中的'\n'
将转换为默认行分隔符os.linesep
。对于stdout和stderr,输出中的所有行结尾都将 转换为'\n'
。有关更多信息,请参阅文档 当换行参数为io.TextIOWrapper
class时 构造函数是None
。
有关详细信息,请查看io.TextIOWrapper()
documentation。
运行echo -n "hello world!"
shell命令并返回不带check_output()
且不使用universal_newlines=True
的文字:
#!/usr/bin/env python
import locale
from subprocess import Popen, PIPE
charset = locale.getpreferredencoding(False)
with Popen(['echo', 'Hello world!'], stdout=PIPE) as process:
output = process.communicate()[0].decode(charset).strip()
这是显示couple of code examples的how subprocess
pipes and TextIOWrapper
class could be used together。
要了解Python中的文本内容和二进制数据是什么,请阅读Unicode HOWTO。这是最重要的部分:Python中有两种主要的字符串类型:表示二进制数据的字节串(一个字节序列)和表示人类可读文本的Unicode字符串(一系列Unicode代码点)。将一个转换为另一个(☯)很简单:
unicode_text = bytestring.decode(character_encoding)
bytestring = unicode_text.encode(character_encoding)
答案 2 :(得分:0)
另外:我相信第一个输出是类型的
bytes
,但是 第二个输出的类型是什么?我的猜测是str
使用UTF-8 编码
关闭,但不是很正确。在Python3中,str
类型由Unicode 代码点索引(请注意,代码点通常但不总是与用户感知的字符具有1:1的对应关系)。因此,当使用str
类型时,底层编码被抽象掉 - 认为它是未编码的,即使从根本上不是这种情况。它是bytes
类型,它被索引为一个简单的字节数组,因此必须使用特定的encoding,在这种情况下(如在大多数类似的用法中),ASCII
就足以解码子进程脚本生成的内容。
Python2对str
类型(see here)的解释有不同的默认值,因此字符串文字在该语言版本中的表示方式不同(这种差异可能是研究时的一大绊脚石)文字处理)。
作为一个主要使用C ++的人,我发现以下内容对于Unicode文本的实际存储,编码和索引非常有启发性:How do I use 3 and 4-byte Unicode characters with standard C++ strings?
所以问题第一部分的答案是bytes.decode()
:
a = a.decode('ascii') ## convert from `bytes` to 'str' type
虽然只是使用
a = a.decode() ## assumes UTF-8 encoding
由于ASCII是UTF-8的子集,通常会产生相同的结果。
或者,您可以像这样使用str()
:
a = str(a,encoding='ascii')
但请注意,如果您想要“仅内容”表示,则必须指定编码 - 否则它实际上会构建一个str
类型,其内部包含引号字符(包括'b'前缀),这正是问题中显示的第一个输出中发生的事情。
subprocess.check_output
以二进制模式处理数据(返回原始字节序列),但隐含参数universal_newlines=True
基本上告诉它解码字符串并将其表示为 text (使用str
类型)。如果要使用Python的str
函数显示输出(以及“仅内容”),则必须转换为print
类型(在Python3中)。
这种转换的有趣之处在于,出于这些目的,它实际上对数据没有任何作用。在幕后发生的是一个实现细节,但如果数据是ASCII(这种类型的程序非常典型),它基本上只是从一个地方复制到另一个地方而没有任何有意义的翻译。 decode 操作只是hoop jumping来改变数据类型 - 操作看似毫无意义的性质进一步模糊了Python文本处理背后的更大视野(对于没有经验的人)。此外,由于the docs没有明确地(按名称)显示返回类型,因此很难知道从哪里开始寻找适当的转换函数。