
streamtokenizer从system.in读取输入时不会自动结束,必须手动发送eof信号(linux/macos按ctrl+d,windows按ctrl+z)才能退出循环,否则将无限等待输入。
StreamTokenizer 本身并不“感知”用户何时停止输入——它只是持续从底层 Reader 中读取字符,直到遇到真正的流结束(EOF)。而 System.in 对应的标准输入流在程序运行期间始终处于打开状态,除非用户显式触发终端级 EOF 信号,否则 st.nextToken() 永远不会返回 st.TT_EOF,导致 while 循环卡住。
例如以下代码看似合理,实则存在阻塞风险:
import java.io.*;
import java.util.ArrayList;
public class ConsoleTokenReader {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StreamTokenizer st = new StreamTokenizer(br);
st.parseNumbers(); // 显式启用数字解析(推荐)
ArrayList numbers = new ArrayList<>();
while (st.nextToken() != StreamTokenizer.TT_EOF) {
if (st.ttype == StreamTokenizer.TT_NUMBER) {
numbers.add(st.nval); // 正确:st.nval 是 double 类型
}
}
System.out.println("Read " + numbers.size() + " numbers: " + numbers);
br.close();
}
} ⚠️ 关键注意事项:
- ✅ 必须手动发送 EOF:在终端中输入完所有数字后,按 Ctrl+D(macOS/Linux)或 Ctrl+Z(Windows)并回车,才能向 System.in 发送 EOF,使 nextToken() 返回 TT_EOF 并退出循环。
- ❌ 不要依赖“空行”或特殊字符(如 -1)来终止——StreamTokenizer 默认不将空行视为 EOF,且未做额外逻辑时无法识别自定义结束标记。
- ✅ 建议调用 st.parseNumbers(),确保数字被正确识别为 TT_NUMBER 类型(虽然默认已启用,但显式声明更清晰)。
- ⚠️ st.nval 是 double 类型,若只处理整数,应谨慎转换(如 (int) Math.round(st.nval)),避免截断误差。
? 替代建议(更现代、更可控):
对于控制台交互场景,推荐使用 Scanner 替代 StreamTokenizer,语义更直观且支持灵活的终止条件:
import java.util.*;
public class ScannerBasedReader {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
List nums = new ArrayList<>();
System.out.println("Enter integers (press Ctrl+D/Ctrl+Z to finish):");
while (sc.hasNextInt()) {
nums.add(sc.nextInt());
}
System.out.println("Collected: " + nums);
sc.close();
}
} 总结:StreamTokenizer 的 TT_EOF 依赖底层流的真实结束,控制台输入需人工触发 EOF;理解这一机制是避免无限等待的关键。在实际开发中,优先考虑 Scanner 或 BufferedReader.readLine() + 字符串解析,兼顾可读性与可控性。
立即学习“Java免费学习笔记(深入)”;










