【问题标题】:Print only the contents of a string仅打印字符串的内容
【发布时间】:2015-02-18 17:46:03
【问题描述】:

这是我的代码:

#! /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 编码。

【问题讨论】:

  • 你试过解码输出吗?
  • @IgnacioVazquez-Abrams:当然,我试图弄清楚这一点,但我最初的几个猜测并没有成功。我希望有人可以告诉我如何做到这一点。语法是什么?涉及哪些数据类型?等等。我相信这对于已经知道如何操作的人来说非常容易。希望我能很快成为那些人中的一员。 :)
  • 现在我知道了所有东西的名称,我能够找到the dup。根据该问题产生的点击次数,我认为可以公平地说,子流程模块的文档可以提供更多使用提示,以便为普通 Python 用户提供更轻松的体验。

标签: python python-3.x unicode


【解决方案1】:

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

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

【讨论】:

  • 非常有帮助——感谢您为我指明了正确的方向。
【解决方案2】:

来自subprocess.check_output() docs

默认情况下,此函数将返回编码为字节的数据。这 输出数据的实际编码可能取决于正在执行的命令 调用,因此通常需要在 应用级别。

可以通过将 universal_newlines 设置为 True 来覆盖此行为 如下所述Frequently Used Arguments

如果您点击链接到Frequently Used Arguments;它描述了universal_newlines=True 的作用:

如果universal_newlinesFalse 文件对象stdin、stdout 和 stderr 将作为二进制流打开,并且没有行尾转换 完成了。

如果universal_newlinesTrue,这些文件对象将以 文本流在通用换行模式下使用返回的编码 locale.getpreferredencoding(False)。对于标准输入,行尾字符 输入中的'\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)

【讨论】:

  • 这些文档是我阅读的第一件事(这就是我猜想尝试universal_newlines=True 的方式)。但是,坦率地说,很多事情最初都在我的脑海中,因为我不知道所有的术语是如何转换成数据类型的(我现在知道可以是 bytesstr)和函数调用(特别是它并没有告诉我我应该只打电话给decode(),即使数据没有真正编码)。
  • ...此外,universal_newlines 的描述(和名称)与我之前的经验中的任何内容都不直接匹配。它似乎描述了我通常认为的文本与二进制 I/O 模式,但这通常是 Linux 系统上的 NOP,所以我觉得它没有太大意义。我在这些文档中没有看到关于universal_newlines=True 将返回类型从bytes 更改为str 的明确文档。 ...但是这个答案中捕获了很多好的数据——谢谢!
  • @nobar:我(几乎)确定子进程的文档,如果您想从输出中删除前导/尾随空格,请不要告诉您调用 .strip() 方法。 subprocess 模块使用bytesstr 类型,但不负责教授bytes.decode()。短语文本流暗示结果是str(最后一段是Python中的常识——你不应该指望subprocess教你)。是的,universal_newlines 也不建议我使用“文本模式”——如果您需要编写单源 Python 2/3 兼容代码,这似乎是一个很好的折衷方案。
【解决方案3】:

另外:我相信第一个输出是类型 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')

但请注意,如果您想要“仅内容”表示,则必须在此处指定编码 - 否则它将实际构建一个内部包含引号字符(包括“b”前缀)的 str 类型,即问题中显示的第一个输出中究竟发生了什么。


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

这种转换的有趣之处在于,出于这些目的,它实际上并没有对数据做任何事情。幕后发生的事情是一个实现细节,但如果数据是 ASCII(对于这种类型的程序来说非常典型),它基本上只是从一个地方复制到另一个地方而没有任何有意义的翻译。 decode 操作只是hoop jumping 来更改数据类型——而看似毫无意义的操作性质进一步混淆了 Python 文本处理背后的更大愿景(对于初学者而言)。此外,由于the docs 没有明确返回类型(按名称),因此甚至很难知道从哪里开始寻找合适的转换函数。

【讨论】:

  • 显示对象的类型:print("a=["+str(a)+"], type="+str(type(a)))
  • 如果他们只是将universal_newlines=True 设为默认值,我可能不会遇到任何问题,也不会问这个问题。如果您的子进程返回非 ASCII Unicode(在我的经验中这种情况很少见),那么您会很乐意处理这个转换过程。如果您的子进程正在返回非文本二进制文件,那么二进制返回模式会很好,但也许应该这样命名。
  • 从好的方面来说,我终于可以说我实际上已经使用 Unicode 进行了编程——尽管它仅用于 ASCII subset 并且没有实际用途。
  • 自我注意:在尝试使用 Python3 之前请三思。 It is a black hole of wasted time。不过仍然是一个很好的计算器。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-05
  • 1970-01-01
  • 2017-01-18
  • 2019-04-20
  • 1970-01-01
相关资源
最近更新 更多