BufferedReader 不参与 StreamTokenizer 的分词逻辑,仅提供高效缓冲的字符输入源;StreamTokenizer 才负责实际解析,其构造器要求 Reader 类型,故需 InputStreamReader 转码、BufferedReader 缓冲以减少 I/O 开销并支持 UTF-8 等编码。

BufferedReader 本身不直接参与 StreamTokenizer 的记号解析,它只是为 StreamTokenizer 提供字符输入源;真正负责分词的是 StreamTokenizer,而 BufferedReader 起到高效、带缓冲的字符读取作用——这是配合的关键前提。
为什么用 BufferedReader 包装 InputStream 或 Reader?
StreamTokenizer 构造器只接受 Reader 类型参数,不能直接接 InputStream(如 FileInputStream)。若要读取文件或网络流,必须先用 InputStreamReader 转码,再套一层 BufferedReader 提升性能:
- 避免每次 nextToken() 都触发底层 I/O,BufferedReader 缓存整块数据,显著减少系统调用
- 支持按行预读、标记重置(mark/reset),便于实现回退逻辑(虽然 StreamTokenizer 自身不自动回退)
- 处理中文、UTF-8 等编码时,必须通过 InputStreamReader 指定 charset,BufferedReader 在其基础上缓冲
初始化 StreamTokenizer 的典型写法
正确链路是:FileInputStream → InputStreamReader → BufferedReader → StreamTokenizer:
try (FileInputStream fis = new FileInputStream("data.txt");
InputStreamReader isr = new InputStreamReader(fis, StandardCharsets.UTF_8);
BufferedReader br = new BufferedReader(isr);
StreamTokenizer st = new StreamTokenizer(br)) {
<pre class="brush:php;toolbar:false;">st.resetSyntax(); // 清空默认规则
st.wordChars('a', 'z'); // a-z 作为单词字符
st.wordChars('A', 'Z');
st.wordChars('0', '9');
st.whitespaceChars(0, ' '); // 空格及以下视为分隔符
st.commentChar('#'); // # 开始单行注释
st.quoteChar('"'); // 双引号内为字符串字面量
st.ordinaryChar('/'); // / 不再是特殊符号(避免干扰)
int token;
while ((token = st.nextToken()) != StreamTokenizer.TT_EOF) {
switch (token) {
case StreamTokenizer.TT_WORD:
System.out.println("WORD: " + st.sval);
break;
case StreamTokenizer.TT_NUMBER:
System.out.println("NUMBER: " + st.nval);
break;
case '"':
System.out.println("STRING: " + st.sval);
break;
default:
System.out.println("OTHER: " + (char)token);
}
}}
常见陷阱与应对建议
StreamTokenizer 行为隐含细节多,容易出错:
- 默认识别“.”为数字一部分,但不识别科学计数法:123.45 可读为 TT_NUMBER,但 1e5 会被拆成 WORD+OTHER。需手动扩展 parseNumber() 或改用 Scanner
- 换行符被当 whitespace 处理,但 st.lineno() 才反映真实行号:调用 st.lineno() 获取当前行号,而非靠自己计数
- 遇到错误字符(如未定义的符号)返回其 ASCII 值:比如 $ 返回 36,需提前用 ordinaryChar('$') 或 wordChars 设置行为
- 字符串中转义不自动处理:\" 或 \n 需自行在 st.sval 中解析,StreamTokenizer 只去掉首尾引号、保留原样
替代方案提醒
StreamTokenizer 是 JDK 旧 API,功能有限且线程不安全。现代 Java 更推荐:
- 对结构化文本(JSON、CSV、配置文件)用专用库(Jackson、OpenCSV、Apache Commons Configuration)
- 对简单分词用 String.split() 或 Scanner(支持正则、类型转换更直观)
- 对复杂语法(DSL、表达式)用 ANTLR 或手写递归下降解析器
只有在维护遗留代码、或需极轻量级词法扫描且格式高度可控时,才值得继续用 StreamTokenizer 配合 BufferedReader。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











