1. 简介
在本篇指南中,我们将学习如何使用 Java 中的 StreamTokenizer
类,将字符流解析成一个个有意义的 token。这个类虽然古老,但在某些场景下依然实用,比如解析配置文件、简易脚本语言等。
2. StreamTokenizer 概述
StreamTokenizer
是一个逐字符读取输入流的工具类。每个字符可以具有以下一种或多种属性:
- 空格(whitespace)
- 字母(alphabetic)
- 数字(numeric)
- 字符串引号(string quote)
- 注释字符(comment character)
默认配置说明
在默认配置下,StreamTokenizer 将以下字符视为特定类型:
类型 | 示例字符 |
---|---|
单词字符(Word) | a-z, A-Z |
数字字符 | 0-9 |
空格字符 | ASCII 0~32(如空格、换行) |
注释字符 | / |
字符串引号 | ' 和 " |
⚠️ 注意:默认情况下,换行符被视为 whitespace,而不是独立的 token,并且不支持 C/C++ 风格的注释。
常用字段说明
StreamTokenizer
提供了几个常量用于判断 token 类型:
TT_EOF
:流结束TT_EOL
:行结束TT_NUMBER
:数字 tokenTT_WORD
:单词 token
同时,它提供了几个字段用于获取当前 token 的值:
ttype
:当前 token 的类型sval
:如果是字符串或单词,保存其值nval
:如果是数字,保存其值(double 类型)
3. 默认配置示例
我们来看一个使用默认配置的完整示例:
private static final int QUOTE_CHARACTER = '\'';
private static final int DOUBLE_QUOTE_CHARACTER = '"';
public static List<Object> streamTokenizerWithDefaultConfiguration(Reader reader) throws IOException {
StreamTokenizer streamTokenizer = new StreamTokenizer(reader);
List<Object> tokens = new ArrayList<>();
int currentToken = streamTokenizer.nextToken();
while (currentToken != StreamTokenizer.TT_EOF) {
if (streamTokenizer.ttype == StreamTokenizer.TT_NUMBER) {
tokens.add(streamTokenizer.nval);
} else if (streamTokenizer.ttype == StreamTokenizer.TT_WORD
|| streamTokenizer.ttype == QUOTE_CHARACTER
|| streamTokenizer.ttype == DOUBLE_QUOTE_CHARACTER) {
tokens.add(streamTokenizer.sval);
} else {
tokens.add((char) currentToken);
}
currentToken = streamTokenizer.nextToken();
}
return tokens;
}
测试内容
我们测试的输入文件内容如下:
3 quick brown foxes jump over the "lazy" dog!
#test1
//test2
输出结果
Number: 3.0
Word: quick
Word: brown
Word: foxes
Word: jump
Word: over
Word: the
Word: lazy
Word: dog
Ordinary char: !
Ordinary char: #
Word: test1
关键字段说明
ttype
:表示当前 token 的类型sval
:当 token 是单词或字符串时,保存其字符串值nval
:当 token 是数字时,保存其 double 值
例如,字符串 "lazy"
的 ttype
是 '"'
,而 sval
是 "lazy"
。
4. 自定义配置
StreamTokenizer
提供了多种方法用于修改默认行为,从而满足不同场景的需求。
示例:修改配置
public static List<Object> streamTokenizerWithCustomConfiguration(Reader reader) throws IOException {
StreamTokenizer streamTokenizer = new StreamTokenizer(reader);
List<Object> tokens = new ArrayList<>();
streamTokenizer.wordChars('!', '-');
streamTokenizer.ordinaryChar('/');
streamTokenizer.commentChar('#');
streamTokenizer.eolIsSignificant(true);
// 以下代码与之前相同
int currentToken = streamTokenizer.nextToken();
while (currentToken != StreamTokenizer.TT_EOF) {
// ...
currentToken = streamTokenizer.nextToken();
}
return tokens;
}
新输出结果
Word: "lazy"
Word: dog!
Ordinary char:
Ordinary char:
Ordinary char: /
Ordinary char: /
Word: test2
配置说明
wordChars('!', '-')
:将!
到-
之间的字符设为单词字符ordinaryChar('/')
:将/
设为普通字符(不再是注释)commentChar('#')
:将#
设为新的注释字符eolIsSignificant(true)
:将换行符视为独立 token
这样一来,我们可以更灵活地控制解析行为,比如将 #
后的内容跳过、将 /
作为普通字符处理等。
其他配置方法
除了上述配置外,还可以使用以下方法进行更复杂的设置:
quoteChar(int ch)
:设置新的字符串引号字符whitespaceChars(int low, int hi)
:设置新的空格字符范围parseNumbers()
:启用数字解析(默认已启用)resetSyntax()
:重置语法状态
通过组合这些方法,你可以实现非常灵活的字符流解析逻辑。
5. 小结
本篇指南中,我们学习了 Java 中 StreamTokenizer
的基本使用方式,包括:
✅ 默认配置下的 token 解析
✅ 如何根据需要修改配置
✅ 常用字段 ttype
, sval
, nval
的作用
✅ 灵活使用各种方法实现自定义词法分析
虽然 StreamTokenizer
类较为古老,但在处理简单文本解析时仍是一个实用的工具。如果你正在编写一个小型脚本解析器、配置文件读取器等,不妨考虑使用它。
如需完整代码,可前往 GitHub 查看。