“scanner”这个名字有点误导人,因为这个词经常被用来指词法分析器,而这不是scanner的意思。它只是
scanf()
在C、Perl中找到的函数,
等
. 像StringTokenizer和
split()
,它被设计为向前扫描,直到找到与给定模式匹配的内容,并且不管在路上跳过什么,都会作为令牌返回。
另一方面,词汇分析器必须检查和分类每个字符,即使它只是决定是否可以安全地忽略它们。这意味着,在每次匹配之后,它可以应用几个模式,直到找到一个匹配的模式。
从那一点开始
.否则,它可能会找到序列“//”,并认为它找到了注释的开头,当它确实在字符串文字中,并且没有注意到开头的引号时。
当然,它实际上比这复杂得多,但我只是说明为什么像StringTokenizer这样的内置工具,
分裂()
扫描仪不适合做这种工作。然而,对于有限的词法分析形式,可以使用Java的正则表达式类。事实上,添加scanner类使其更容易,因为添加了新的matcher API来支持它,即区域和
usePattern()
方法。下面是一个在Java ReGEX类之上构建的基本扫描仪的例子。
import java.util.*;
import java.util.regex.*;
public class RETokenizer
{
static List tokenize(String source, List rules)
{
List tokens = new ArrayList();
int pos = 0;
final int end = source.length();
Matcher m = Pattern.compile("dummy").matcher(source);
m.useTransparentBounds(true).useAnchoringBounds(false);
while (pos < end)
{
m.region(pos, end);
for (Rule r : rules)
{
if (m.usePattern(r.pattern).lookingAt())
{
tokens.add(new Token(r.name, m.start(), m.end()));
pos = m.end();
break;
}
}
pos++; // bump-along, in case no rule matched
}
return tokens;
}
static class Rule
{
final String name;
final Pattern pattern;
Rule(String name, String regex)
{
this.name = name;
pattern = Pattern.compile(regex);
}
}
static class Token
{
final String name;
final int startPos;
final int endPos;
Token(String name, int startPos, int endPos)
{
this.name = name;
this.startPos = startPos;
this.endPos = endPos;
}
@Override
public String toString()
{
return String.format("Token [%2d, %2d, %s]", startPos, endPos, name);
}
}
public static void main(String[] args) throws Exception
{
List rules = new ArrayList();
rules.add(new Rule("WORD", "[A-Za-z]+"));
rules.add(new Rule("QUOTED", "\"[^\"]*+\""));
rules.add(new Rule("COMMENT", "//.*"));
rules.add(new Rule("WHITESPACE", "\\s+"));
String str = "foo //in \"comment\"\nbar \"no //comment\" end";
List result = RETokenizer.tokenize(str, rules);
for (Token t : result)
{
System.out.println(t);
}
}
}
顺便说一句,这是我发现的唯一一个很好的用途
lookingAt()
方法。D