我相信我找到了解决此问题的纯 TCL 方法:将 EOF 字符更改为 Ctrl-Z 以外的其他字符,读取一个虚拟行(从输入缓冲区中删除 Ctrl-Z),然后重置EOF 字符返回到Ctrl-Z。总结在一个过程中:
proc clearEOF {} {
fconfigure stdin -eofchar { "\x01" "" }
gets stdin dummy
fconfigure stdin -eofchar { "\x1a" "" }
}
\x01 的选择有些武断:基本上任何不太可能与Ctrl-Z 一起出现在输入缓冲区中的东西都应该这样做。
注意:这仅在带有 TCL 8.6.9 的 Windows 10 上进行了测试。
原始测试程序
puts "Enter lines then Ctrl-Z <RETURN> to end"
while { [ gets stdin line ] >= 0 } {
puts "Read: $line"
}
puts "Reached EOF"
puts "eof=[eof stdin]"
puts "Enter another line"
puts "gets=[gets stdin line]"
puts "Read: $line"
希望是在阅读了多行后,由 EOF 标记 (Ctrl-Z) 终止,然后您可以阅读另一行。在实践中,EOF-state没有被清除,第二次调用gets不等待输入,立即返回-1(=EOF):
Enter lines then Ctrl-Z <RETURN>
Line1
Read: Line1
Line2
Read: Line2
^Z
Reached EOF
eof=1
Enter another line <-- This does not wait
gets=-1
Read:
注意:尽管TCL documentation 包括(我的重点):
read ?-nonewline? fileID
从 fileID 中读取所有剩余的字节,并返回该字符串。如果设置了 -nonewline,则如果最后一个字符是换行符,则将丢弃它。 在执行读取命令之前清除任何现有的文件结束条件。
用set line [ read stdin ] 之类的东西替换gets 没有区别。两个命令都立即返回。多次重复任一命令没有区别:一旦 TCL(和/或 Windows1)认为我们已经达到 EOF,我们停留 EOF!
我的解决方案
经过一番尝试,尝试了 TCL 拥有的每个文件操作命令,我想出了以下内容:
puts "Enter lines then Ctrl-Z <RETURN>"
while { [ gets stdin line ] >= 0 } {
puts "Read: $line"
}
puts "Reached EOF"
puts "eof=[eof stdin]"
puts "Reset EOF char"
fconfigure stdin -eofchar { "\x01" "" }
puts "eof=[eof stdin]"
puts "Reading dummy line"
puts "gets=[gets stdin line]"
fconfigure stdin -eofchar { "\x1a" "" }
puts "Enter another line"
puts "gets=[gets stdin line]"
puts "Read: $line"
this 版本的输出确实 等待更多输入:
Enter lines then Ctrl-Z <RETURN>
Line 1
Read: Line 1
Line 2
Read: Line 2
^Z
Reached EOF
eof=1
Reset EOF char
eof=0 <-- EOF has been cleared
Reading dummy line
gets=1 <-- Read 1 character: the Ctrl-Z
Enter another line
More text <-- Waits for this to be typed
gets=9
Read: More text
我对正在发生的事情的假设是更改 EOF 字符确实重置 EOF 状态(无论这种情况发生在“TCL”还是“Windows“我不确定)。使用不同的 EOF 标记,我们可以读取包含留在输入缓冲区中的 Ctrl-Z 的行。 (根据您在Ctrl-Z 的任一侧输入的内容,这通常还包含一个行尾标记)。处理完Ctrl-Z 后,我们可以将EOF 字符back 重置为Ctrl-Z 并照常继续从stdin 读取。
1微软 WSL GitHub 页面上的This issue 表明可能是 Windows 有问题:一旦缓冲区中的 Ctrl-Z 总是返回 EOF ,即使使用了clearerr()。我对“过去 30 年来 xplat 程序员的另一个祸根,Unix 上的 Ctrl-D 和 Windows 上的 Ctrl-Z 的工作方式不同。”的阅读是,虽然问题是针对 WSL, 问题在 Windows 本身。有趣的是,最终评论(在撰写本文时)声明“已在 Windows Insider Build 18890 中修复”,但可能仍需要致电 clearerr()。