在Python 3中将LF打印到Windows标准输出

15

如何在Windows上将\n打印到标准输出?此代码适用于Python 2,但不适用于Python 3:

# set sys.stdout to binary mode on Windows
import sys, os, msvcrt
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)

# the length of testfile created with
#     python test_py3k_lf_print.py > testfile
# below should be exactly 4 symbols (23 0A 23 0A)
print("#\n#")
1个回答

15
Python 3已经将标准I/O配置为二进制模式,但它有自己的I/O实现来进行换行符转换。不要使用需要文本模式文件的print,而应手动调用sys.stdout.buffer.write以使用二进制模式的BufferedWriter。如果需要使用print,则需要一个不使用通用换行符的新文本I/O包装器。例如:
stdout = open(sys.__stdout__.fileno(), 
              mode=sys.__stdout__.mode, 
              buffering=1, 
              encoding=sys.__stdout__.encoding, 
              errors=sys.__stdout__.errors, 
              newline='\n', 
              closefd=False)

由于closefd为false,关闭此文件不会关闭原始的sys.stdout文件描述符。您可以通过print("#\n#", file=stdout)明确使用此文件,或者替换sys.stdout = stdout。原始文件可用作sys.__stdout__
背景
Python 3的io模块旨在提供跨平台和跨实现(CPython、PyPy、IronPython、Jython)规范,以RawIOBaseBufferedIOBaseTextIOBase这些抽象基类表示所有类似文件的对象。它包括_pyio模块中的参考纯Python实现。原始io.FileIO实现的共同点是一组低级POSIX系统调用,如readwrite,这消除了CRT stdio不一致的问题。在Windows上,POSIX层只是CRT的低I/O层,但至少限于单个平台的怪癖。

Windows的一个怪癖是在其POSIX I/O层中具有非标准文本和二进制模式。Python通过始终使用二进制模式并对stdio文件描述符调用setmode来解决这个问题1

Python可以通过实现RawIOBaseWinFileIO注册子类来避免使用Windows CRT进行I/O。这里有一个issue 12939的建议补丁。另一个例子是win_unicode_console模块,它实现了WindowsConsoleRawReaderWindowsConsoleRawWriter类。


1. 这给嵌入Python并期望stdio使用默认文本模式的程序带来了问题。例如,在二进制模式下打印宽字符字符串不再像在ANSI文本模式下一样转换为char,当然也不会像在UTF-16文本模式下使用WriteConsoleW进行打印。例如:

Python 2.7.10 (default, May 23 2015, 09:44:00) 
[MSC v.1500 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys, os, msvcrt, ctypes 
>>> ctypes.cdll.msvcr90.wprintf(b'w\x00i\x00d\x00e\x00\n\x00') 
wide
5
>>> msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY) 
16384
>>> ctypes.cdll.msvcr90.wprintf(b'w\x00i\x00d\x00e\x00\n\x00')
w i d e
 5


对于嵌入 Python 并期望使用默认文本模式的程序感到悲痛 - 是谁期望这样做,为什么?对我来说,在 Windows 上这是一个主要的头痛,因为它会破坏重定向的二进制流。 - anatoly techtonik
1
人们将wchar_t字符串打印到控制台时,期望文本模式,因为CRT至少将它们强制转换为char(对于ASCII来说足够好)。而在二进制模式下,它只是写入原始的宽字符,就像这样p r i n t s l i k e t h i s - Eryk Sun
天啊,一个整整的蚯蚓罐。=) - anatoly techtonik
我希望看到类似Drekin的win_unicode_console实现的C语言版本并纳入Python 3.6或至少3.7中。这将用于控制台I/O,而不是使用io.FileIO。对于管道、磁盘文件或非控制台字符设备(例如\\.\NUL),标准I/O仍将使用io.FileIO。此外,我希望看到Python 4通过整合问题12939提供的补丁彻底脱离CRT I/O。将Windows强制转换为POSIX环境过于限制性,它们具有不同的优点和缺点。 - Eryk Sun
2
自动换行的转换可能会很难追踪。当我的 print 输出在atom的 process-pallette 中查看时,似乎双倍换行时,我知道出了什么问题,但我不知道如何禁用通用换行符转换 - 当然,我直接调用 sys.stdout.write() 也失败了 - 更接近问题,但仍在错误的一端。您重新定义 sys.stdout 的代码完美地运行,谢谢。 - jedwards
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接