【问题标题】:Changing the way stdin/stdout is opened in Python 3更改在 Python 3 中打开 stdin/stdout 的方式
【发布时间】:2021-02-26 13:37:52
【问题描述】:

在最近的 Python 3.x 中,open 的默认行为是以 通用换行符 模式 (newline=None) 打开文件,这意味着如果我打开这样的文件:

f = open("file.txt")

然后在使用 f.readline()for line in f: 等时,所有不同的行尾 (CRLF/CR/LF) 都会转换为 "\n"

但是,在处理sys.stdin时,不同的行尾没有被转换(至少在Linux上,读取一个CRLF文件意味着sys.stdin.readline()的结果以"\r\n"结尾)。这意味着sys.stdin 是使用不同的newline 设置打开的。有什么方法可以影响打开sys.stdin时使用的参数吗?

更一般地说,Python 中是否有类似于 Perl 的 binmode 的东西可以改变从 sys.stdin 读取/写入到 sys.stdout 的工作方式?

为了清楚起见,我知道我可以自己进行 CRLF→LF 转换。这不是这个问题的目的。

【问题讨论】:

  • 在 Python 3.x 中,stdoutstdin 只是普通的 TextIOWrapper 对象,因此您不需要 类似 binmode 的对象;你可以访问sys.stdout.buffer 来获取二进制文件(或sys.stdout.buffer.raw 来获取它下面的原始无缓冲文件)。这能满足您的需求吗?
  • 我猜肯定有一种方法可以将标准输入作为文件打开(使用 open 命令),至少使用 linux(虽然我找不到)。
  • @abarnert 我希望sys.stdin 的行为与open 返回的文件句柄相同,并且我希望能够为open 指定参数(例如newline)。我如何使用底层二进制文件或原始文件来实现这一点?
  • 嗯,你的问题是问多个问题。使用bufferbuffer.raw 为您提供相当于Perl 的binmode,这就是您所说的“更普遍地”想要的。你实际上不能让sys.stdin 表现得像sys.stdin.buffer(好吧,你总是可以做sys.stdin = sys.stdin.buffer,但这样做会中断对input 的调用,以及任何期望stdin 成为文本文件的库,并且会让任何读者感到困惑,所以我想它更shouildn't而不是can't),但你可以使用sys.stdin.buffer
  • 您似乎也在问是否可以将相同的 fd 包装在一个新的文件对象中?这很简单:myin = open(sys.stdin.fileno(), <whatever options you want>),然后使用myin。在这种情况下,如果您以文本模式打开,您可以安全地sys.stdin = myin 而不会破坏任何内容。

标签: python python-3.x


【解决方案1】:

没有办法完全替代 Python 在sys.stdinsys.stdout 中包装标准输入和标准输出管道的方式。 (有一些有限的控制,比如the -u option`,仅此而已。)

但是,您要求的还有很多其他的东西,所有这些都是可能的,其中一些可能是您想要的。


首先,sys.stdin 只是一个普通的TextIOWrapper,由open 返回(在文本模式下)。这意味着您可以访问它的底层二进制对象,或者它下面的原始无缓冲文件对象,或者它下面的 OS 文件描述符,与任何其他文本文件相同:

sys.stdin.buffer
sys.stdin.buffer.raw
sys.stdin.fileno()

通常,这就是您所需要的。您不想实际替换sys.stdin = sys.stdin.buffer。这会破坏inputfileinput 模块,谁知道还有什么期望stdin 成为文本文件。但是您可以只使用sys.stdin.buffer 而不是sys.stdin,它大致相当于我认为您在perl 的binmode 中寻找的内容。


如果您想要一个 TextIOWrapper,并在同一个基础文件周围包含不同的选项,您也可以这样做。

对于最简单的情况,最简单的方法就是按照您想要调用open 的方式调用open,并传递文件描述符:

sin = open(sys.stdin.fileno(), <your open arguments here>)

对于不那么琐碎的情况,您可能需要阅读TextIOWrapper 文档(以及BufferedReaderio 模块中的其他类型),并以您想要的方式完全包装您想要的部分。但通常情况下,open 就足够了。

无论哪种方式,您都可以使用sin 代替sys.stdin

或者,既然现在一个非常好的TextIOWrapper(假设你opened在文本模式下),替换@987654349是安全和合理的 @:

sys.stdin = sin

...现在input 等将继续工作,并且会按照您希望的方式工作。

【讨论】:

    【解决方案2】:

    您可以reconfigure() sys.stdin 使用例如:

    sys.stdin.reconfigure(newline=None)
    

    这应该与使用 open() 以文本模式打开的任何其他文件的行为相匹配。

    请注意,必须在第一次读取流之前完成重新配置。

    【讨论】:

      【解决方案3】:

      可以这样解决:

      sys.stdin = io.TextIOWrapper(sys.stdin.buffer)
      

      这会导致sys.stdin 的行为就像使用open 和默认参数(包括newline=None)打开它一样。 TextIOWrapper 的构造函数采用与open 相同的参数,因此我们还可以执行以下操作:

      sys.stdin = io.TextIOWrapper(sys.stdin.buffer, newline="", encoding="utf-8")
      sys.stdout = io.TextIOWrapper(sys.stdout.buffer, newline="\r\n")
      

      等等

      因此,我们有一种方法可以影响打开标准输入/标准输出的方式,类似于 Perl 的 binmodebinmode FILEHANDLE, LAYERS 版本)。

      感谢 @abarnert 对 TextIOWrapper 的评论。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-05-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-06-02
        • 2018-10-22
        • 1970-01-01
        相关资源
        最近更新 更多