此代码在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导致输出被截断(从0
到2666
)?
(使用python -u test.py > a.txt
将输出重定向到文件时,它可以正常工作。)
也许有关缓冲的事情?
答案 0 :(得分:2)
通过WINAPI WriteFile
和WriteConsoleW
编写的控制台的大小被记录为具有模糊定义的限制,如下所示:
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系统服务的设备,包括NtCreateFile
,NtReadFile
,NtWriteFile
和NtDeviceIoControlFile
。
控制台应用程序不知道有多少堆可用于写操作。 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中删除。