为什么此代码在Windows和Linux之间打印出不同的结果?

时间:2019-01-19 10:41:58

标签: python python-3.x windows powershell

此代码在Windows和Linux之间打印了不同的字符串。

test.py:

print(";".join([str(i) for i in range(10000)]))

平台:x86_64 Linux 4.4 .0-17763-微软
Python版本:3.7.2
终端:bash,鱼

缩写输出:

$ python --version
Python 3.7.2
$ python test.py
0;1;2;3;4;5;6....9997;9998;9999
$ python -u test.py
0;1;2;3;4;5;6....9997;9998;9999

平台:Windows 10 1809
Python版本:3.6.8、3.7.0、3.7.2
终端:cmd,powershell

缩写输出:

./python --version
Python 3.6.8
./python test.py
0;1;2;3;4;5;6....9997;9998;9999
./python -u test.py
0;1;2;3;4;5;6....2663;2664;2665;26
./python --version
Python 3.7.0
./python test.py
0;1;2;3;4;5;6....9997;9998;9999
./python -u test.py
0;1;2;3;4;5;6....2663;2664;2665;26
./python --version
Python 3.7.2
./python test.py
0;1;2;3;4;5;6....9997;9998;9999
./python -u test.py
0;1;2;3;4;5;6....2663;2664;2665;26

那么为什么在Windows中-u arg导致输出被截断(从02666)?
(使用python -u test.py > a.txt将输出重定向到文件时,它可以正常工作。)

也许有关缓冲的事情?

1 个答案:

答案 0 :(得分:2)

通过WINAPI WriteFileWriteConsoleW编写的控制台的大小被记录为具有模糊定义的限制,如下所示:

  

nNumberOfCharsToWrite [in]
  要写入的字符数。如果指定的总大小   字符数超过可用堆,函数失败并显示    ERROR_NOT_ENOUGH_MEMORY

没有记录这是指“堆”。一个进程可以具有多个各种大小的堆(固定堆或动态堆)。 NT运行时库(例如RtlCreateHeap)中的本机堆实现可以在指定地址创建堆,从而可以方便地访问与其他进程共享的内存。使用共享堆通常与Local Inter-Process Communication(LPC)端口或NT 6.0+中的异步LPC结合使用。 LPC端口用于在应用程序和系统服务之间传递消息,例如会话管理器(smss.exe),服务控制管理器(services.exe),本地安全机构(lsass.exe),桌面会话服务器(csrss.exe) ,以及控制台主机的实例(conhost.exe)。直接排队到LPC端口的消息被限制为256个字节。通过将消息排队到引用共享内存的端口来传递更大的消息。

事实证明,控制台的旧版本(NT 6.3之前的版本)使用LPC作为I / O通道,并且上述堆仅 64 KiB 。这是设计的特殊选择。我认为有人在喝太多的用户模式子系统,通过消息传递Kool-Aid。正确的NT I / O使用具有I / O系统服务的设备,包括NtCreateFileNtReadFileNtWriteFileNtDeviceIoControlFile

控制台应用程序不知道有多少堆可用于写操作。 Python可以从64 KiB开始,然后逐步下降,但其raw file I/O要求每个调用一个系统调用。取而代之的是,写入上限为32 KiB,应该会成功。此限制允许编写最多16K UTF-16代码点的宽字符字符串。麻烦之处在于,控制台I / O堆栈使用3.6+版本中的UTF-8,必须通过MultiByteToWideChar对其进行解码。目前,它只是重复将UTF-8缓冲区分成两半,直到结果长度小于16K。因此,在问题的示例中,写作48,889个字符减半为24,444个字符,再减半为12,222个字符。 (IMO,最好尝试写入最多16K代码点;获取实际写入的数字,然后在子字符串上调用WideCharToMultiByte以确定写入的UTF-8字节数。当前设计实际上存在一个错误如果UTF-8 2-4字节序列与剪切点重叠。)

在NT 6.3+(Windows 8.1+)中,控制台I / O没有此大小限制,因为它使用ConDrv设备和I / O系统调用而不是LPC。但是,仅用-u命令行选项配置的代码来支持无缓冲文本I / O堆栈就不值得使用特殊的代码了。我们希望缓冲交互式控制台I / O。正常的open调用实际上不允许使用无缓冲的文本I / O。例如:

>>> open('conout$', 'w', buffering=0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: can't have unbuffered text I/O

对Windows 7的扩展支持将于2020年1月14日终止,因此Python 3.8将是最后一个支持它的版本。控制台写限制应在Python 3.9中删除。