代码之家  ›  专栏  ›  技术社区  ›  Armen Michaeli

java.util.Scanner是如何工作的?

  •  1
  • Armen Michaeli  · 技术社区  · 12 年前

    我有一种简单的语言,它由以下模式组成

    size(50*50)
    start(10, 20, -x)
    forward(15)
    stop
    

    这是乌龟绘画语言的一个例子。我需要正确地标记它。上面是一个源代码实例。语句和表达式用换行符分隔。我将“扫描仪”设置为使用换行符之类的分隔符。我想 next("start") 吃字符串“开始”,然后我发布 next("(") 吃第一个括号。然而,它似乎做了一些超出我预期的事情。扫描仪是否已经根据分隔符将上述内容分解为标记,和/或我是否需要以不同的方式处理?对我来说,第一行的“start”、“(”、“50”、“*”、“50%”和“)”将构成单独的令牌,这在这里似乎是一个未实现的期望。如何用尽可能少的代码来标记上述内容?我目前不需要写令牌化器,我正在写一个解释器,所以令牌化是我目前不想花时间的事情,我只是喜欢Scanner在这里和我一起工作。

    我的 useDelimiter 调用如下:

    Scanner s ///...
    s.useDelimiter(Pattern.compile("[\\s]&&[^\\r\\n]"));
    

    先发布 next 调用将提供整个文件的内容。如果没有上面的电话,它给了我完整的第一行。

    2 回复  |  直到 12 年前
        1
  •  3
  •   Joe    12 年前

    要编写一个合适的解析器,您需要用形式语法定义您的语言。相信我,你想把它做好,否则你会在下游遇到问题。

    你可能可以在最低级别将你的标记表示为正则表达式,但首先你需要清楚你的语法,即词汇结构中标记的组合。您可以将其表示为递归函数(方法),称为Productions。每个Production函数都可以使用scanner来测试它是否正在查看它想要的令牌。但扫描仪会消耗输入,您无法反转。

    如果您使用扫描仪,您会发现以下内容不合适:

    1. 它将始终根据正则表达式来解析令牌,

      1.1因此,即使你得到了一个可以使用的令牌,你也必须编写更多的代码来决定它到底是什么令牌

      1.2你可能无法将你的语言语法表示为一个大的表达式

    2. 你不能重新卷绕。前瞻性解析器(像您这样的许多语法都需要)需要能够前瞻性地查看输入流,然后决定(如果需要)不使用输入,而是让另一个令牌解析器函数使用它。

    我建议您自己编写字符lexer,并在字符串/字符数组而不是流上进行迭代。然后你就可以重新上发条了。

    否则,使用一个现成的lexer/parser框架,如 yacc Coco/R

        2
  •  2
  •   Community CDub    7 年前

    班级 java.io.StreamTokenizer 可能更适合。它用于此 example recursive descent parser

    附录: 两者的主要区别是什么 StreamTokenizer Scanner ?

    任何一个都可以 lexical analysis 解析器所需的。 流标记器 重量较轻,但仅限于四个预定义的元令牌。 扫描仪 更灵活,但使用起来更麻烦。这是一个 comparison 两个和 variation 关于后者。