【问题标题】:How to create a Tokenizer with the util.regex.Matcher (java)?如何使用 util.regex.Matcher (java) 创建一个 Tokenizer?
【发布时间】:2014-09-06 12:19:54
【问题描述】:

其实我有以下正则表达式标记(学校职责):

Identificator = [a-zA-Z_][a-zA-Z0-9_]*
Integer = [0-9]+
ReservedKeywords = true|false|while|foreach|for|plus
Symbols = *|/|-|\(|\)|
Blank = \s+

我不能使用 Scanner 类,因为某些标记之间可能没有空格。请注意,解析器(假设它接收到正确的标记)已准备就绪,并且还准备了 AST 的类型检查和评估。仅缺少“最简单”的部分,因此“Tokenizer”并且互联网上没有足够完整的示例。

我不明白 util.regex.Matcher 类的文档,很混乱。

其实是合法的

  • [ 保留关键字|标识符|整数 ] 后跟一个符号
  • 符号后跟[ ReservedKeyword|标识符|整数|符号 ]
  • [ 保留关键字|标识符|整数|符号 ] 后跟一个空格
  • 空格后跟[ ReservedKeyword|标识符|整数|符号 ]
  • [ 保留关键字|标识符|整数|符号|空白 ] 后跟流/字符串结束

我们必须使用 Matcher 类,所以无论如何都没有机会硬编码标记器(这太简单了:简单的状态机 + 地图查找,但我们不允许这样做)。

分词器必须有 2 个方法(“hasNext”和“next”)。

我需要一些示例来了解如何使用 Matcher 来匹配带有取决于上下文的分隔符的字符串(Scanner 类不适合,因为会“吃掉”分隔符,而分隔符是语法的一部分,请参见以下示例:

(3 plus 5)*(8/3*7)

它应该被标记为

(.3.plus.5.).*.(.8./.3.*.7.)

我可以使用 "(|)|\s+" 作为分隔符,但扫描仪只会返回

3 plus 5 * 8 / 3 * 7

由于运算符的关联性,结果将是

3 plus (((5*8)/3)*7)

这是不正确的。

我需要做以下事情:

给定一组模式(标识符、整数、保留关键字、符号、空白等)。我需要匹配任何这些模式的第一次出现。分隔符是“符号|空白” 但不应丢弃分隔符,而应将它们作为标记返回。 这必须使用 Matcher 类来完成。

一个示例显示如何使用分隔符“空白|符号”返回或分隔字符串或分隔符本身来标记字符串。

【问题讨论】:

  • 我解决了这个问题,还是这个问题太宽泛了?

标签: java regex matcher


【解决方案1】:

在费尽心思弄清楚 Matcher 是如何工作的之后,我能够创建一个比通常的 Scanner 更复杂的分词器。由于没有人回答这里是相关部分(因为这是学校作业,我可以分享代码):

private final Scanner scanner;
private final String delimiter =  "\\*|/|-|\\(|\\)|";

private final Pattern delim  = Pattern.compile(delimiter);
private Matcher delim_matcher;

private String  region;
private int     regionStart;
private int     regionEnd;
private int     start;
private int     end;

// Called by constructor, I stripped the constructor because trivial
private void Init(){
    scanner = new Scanner(System.in);
    region = scanner.next();
    delim_matcher = delim.matcher(region);
    regionStart = 0;
    regionEnd = region.length();
}

private boolean nextDelimiter(){
    boolean found = delim_matcher.find();
    start = found ? delim_matcher.start() : delim_matcher.regionEnd();
    end = found? delim_matcher.end(): delim_matcher.regionEnd();
    return found;
}

private boolean hasPrefix(){
    return start > regionStart;
}

public TokenType next() throws NoSuchElementException{
    //find next delimiter ( symbol )
    boolean found = nextDelimiter(); //TODO: see breakpoints here

    if(hasPrefix()){

        //there was something before the delimiter (keyword, identificator etc.)
        decodePrefix( region.substring(regionStart,start) );

        if(found)
            delim_matcher.region(start,regionEnd); //reset to match symbol next time

        regionStart = start; //hasPrefix -> false
        return tokenType;   
    }
    else if(!hasPrefix() && found){

        decodeSymbol( region.substring(start,end)); 
        delim_matcher.region(end,regionEnd); //reset to skip already found symbol
        regionStart = end;
        return tokenType;
    }
    else{

        if(scanner.hasNext()){ //next is not a whitespace (because scanner already skip blanks)
            region = scanner.next(); 
            delim_matcher = delim.matcher(region);
            regionStart = 0;
            regionEnd = region.length();
            return next();
        }else
            return tokenType = EOF;
    }
}

public boolean hasNext() {
    return tokenType != EOF; //EOF is a value of the enum "TokenType"
}

正如预期的那样,这个 Tokenizer 比 Scanner 类更有用。 Scanner 类有丢弃分隔符的缺点(因为在解析程序时符号可能是分隔符,我不希望它们被丢弃)。

这个 Tokenizer 使用 Scanner 来检索空白分隔的字符串,然后使用额外的处理来分割符号周围的字符串。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多