仅仅出于我自己的目的,我正在尝试在Java中构建一个tokenizer,我可以在其中定义一个常规语法并根据它来标记输入。不推荐使用StringTokenizer类,我在Scanner中发现了一些暗示我想要做的事情,但还没有运气。有人知道这个问题的好方法吗?
答案 0 :(得分:12)
名称“扫描仪”有点误导,因为这个词通常用于表示词法分析器,而这不是Scanner的用途。它只是你在C,Perl,等中找到的scanf()
函数的替代品。与StringTokenizer和split()
一样,它设计为向前扫描,直到找到给定模式的匹配,并且在途中跳过的任何内容都将作为标记返回。
split()
和Scanner这样的内置工具不适合这种任务。但是,可以将Java的正则表达式类用于有限形式的词法分析。事实上,添加Scanner类使得它更加容易,因为添加了新的Matcher API来支持它,即区域和usePattern()
方法。以下是基于Java正则表达式类构建的基本扫描程序的示例。
import java.util.*;
import java.util.regex.*;
public class RETokenizer
{
static List<Token> tokenize(String source, List<Rule> rules)
{
List<Token> tokens = new ArrayList<Token>();
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<Rule> rules = new ArrayList<Rule>();
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<Token> result = RETokenizer.tokenize(str, rules);
for (Token t : result)
{
System.out.println(t);
}
}
}
顺便说一句,这是我在lookingAt()
方法中找到的唯一好处。 :d
答案 1 :(得分:3)
如果我理解你的问题,那么这里有两个标记字符串的示例方法。您甚至不需要Scanner类,只有在您想要预先生成令牌时,或者比使用数组更加软化地迭代它们。如果数组足够,只需使用下面给出的String.split()。
请提供更多要求以获得更精确的答案。
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
String textToTokenize = "This is a text that will be tokenized. I will use 1-2 methods.";
Scanner scanner = new Scanner(textToTokenize);
scanner.useDelimiter("i.");
while (scanner.hasNext()){
System.out.println(scanner.next());
}
System.out.println(" **************** ");
String[] sSplit = textToTokenize.split("i.");
for (String token: sSplit){
System.out.println(token);
}
}
}
答案 2 :(得分:2)
如果这是一个简单的项目(用于学习如何运作),那么请参考Balint Pato所说的内容。
如果这是针对较大的项目,请考虑使用像JFlex这样的扫描仪生成器。有点复杂,但更快,更强大。
答案 3 :(得分:2)
这里的大多数答案都已经很出色但如果我没有指出ANTLR,我会失职。我围绕这个优秀的工具创建了完整的编译器。版本3有一些惊人的功能,我推荐它用于任何需要你根据明确定义的语法解析输入的项目。