代码之家  ›  专栏  ›  技术社区  ›  eplawless

如何使用Java的扫描器类和正则表达式来标记输入?

  •  5
  • eplawless  · 技术社区  · 16 年前

    为了我自己的目的,我试图在Java中构建一个记录器,在那里我可以定义一个规则语法,并基于它来标记输入。StringTokenizer类已被弃用,我在scanner中发现了几个函数,它们提示我要做什么,但还没有运气。有人知道怎么做吗?

    4 回复  |  直到 12 年前
        1
  •  11
  •   Alan Moore Chris Ballance    16 年前

    “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<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

        2
  •  3
  •   Balint Pato    16 年前

    如果我很好地理解您的问题,那么这里有两个标记字符串的示例方法。您甚至不需要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);
            }
        }
    
    }
    
        3
  •  2
  •   Michael Myers KitsuneYMG    16 年前

    如果这是一个简单的项目(学习事情是如何工作的),那么按照巴林特·帕托所说的去做。

    如果这是一个更大的项目,考虑使用类似 JFlex 相反。有点复杂,但更快更强大。

        4
  •  2
  •   ra9r    15 年前

    这里的大多数答案都已经很好了,但如果我不指出,我会有所疏忽。 ANTLR . 我围绕这个优秀的工具创建了整个编译器。版本3有一些惊人的特性,我建议您在任何需要您根据定义良好的语法分析输入的项目中使用它。