只打印字符串的内容

Print only the contents of a string

这是我的代码:

#! /usr/bin/env python3
import subprocess
a = subprocess.check_output('echo -n "hello world!"',shell=True)
print("a="+str(a))

输出:

a=b'hello world!'

如果我在对 check_output 的调用中包含参数 universal_newlines=True,那么我会得到所需的输出:

a=hello world!

为了更好地理解现代(Unicode)时代文本编程的神秘世界,我想知道如何在不指定universal_newlines=True的情况下生成第二个输出。换句话说,我调用什么函数来转换 a 以便它产生所需的输出。

一个工作示例会大有帮助。详细的解释很好,但对于外行来说它们往往有点令人困惑——可能是由于使用了过多的术语,可能是因为 Python2 和 Python3 之间的差异,或者只是因为我非常在我的工作中很少需要考虑文本编码——我使用的大多数工具不需要像这样的特殊处理。

另外:我相信第一个输出的类型是 bytes,但是第二个输出的类型是什么?我的猜测是 str 使用 UTF-8 编码。

正如 Ignacio 的评论最初暗示的那样,您可以使用 decode:

>>> a = b"hello world!"
>>> print("a="+str(a))
a=b'hello world!'
>>> print("a="+a.decode())
a=hello world!

Also: I believe the first output is of type bytes, but what is the type of the second output? My guess is str with UTF-8 encoding.

接近,但不太正确。在 Python3 和 str type is indexed by Unicode code points (note that code points usually, but not always, have a 1:1 correspondence with user perceived characters). Therefore, the underlying encoding is abstracted away when using the str type -- consider it unencoded, even though that is fundamentally not the case. It is the bytes type which is indexed as a simple array of bytes and therefore must use a particular 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')

但请注意,如果您想要 "contents only" 表示,则必须在此处指定编码 - 否则它实际上会构建一个 str 类型,该类型内部包含引号字符(包括 'b' 前缀),这正是问题中显示的第一个输出中发生的情况。


subprocess.check_output 默认以 binary 模式(returning 原始字节序列)处理数据,但神秘的参数 universal_newlines=True基本上告诉它 解码 字符串并将其表示为 text (使用 str 类型)。如果您想使用 Python 的 print 函数显示输出(和 "only the contents"),则必须转换为 str 类型(在 Python3 中)。

这种转换的有趣之处在于,出于这些目的,它实际上对数据没有任何影响。引擎盖下发生的是一个实现细节,但如果数据是 ASCII(对于此类程序非常典型),它基本上只是从一个地方复制到另一个地方而没有任何有意义的翻译。 decode 操作只是 hoop jumping to change the data type -- and the seemingly pointless nature of the operation further obfuscates the larger vision behind Python's text handling (for the uninitiated). Further, since the docs 不要使 return 类型显式(按名称),甚至很难知道从哪里开始寻找适当的转换函数。

来自 subprocess.check_output() docs:

By default, this function will return the data as encoded bytes. The actual encoding of the output data may depend on the command being invoked, so the decoding to text will often need to be handled at the application level.

This behaviour may be overridden by setting universal_newlines to True as described below in Frequently Used Arguments.

如果按照link到Frequently Used Arguments;它描述了 universal_newlines=True 的作用:

If universal_newlines is False the file objects stdin, stdout and stderr will be opened as binary streams, and no line ending conversion is done.

If universal_newlines is True, these file objects will be opened as text streams in universal newlines mode using the encoding returned by locale.getpreferredencoding(False). For stdin, line ending characters '\n' in the input will be converted to the default line separator os.linesep. For stdout and stderr, all line endings in the output will be converted to '\n'. For more information see the documentation of the io.TextIOWrapper class when the newline argument to its constructor is None.

有关详细信息,您可以查看 io.TextIOWrapper() documentation

到 运行 你的 echo -n "hello world!" shell 命令和到 return 没有 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()

这是 that show 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)