扫描一个数字并在输入流中返回词位 - Java?

时间:2014-08-30 01:07:12

标签: java java.util.scanner lexical-analysis

我正在尝试编写一个方法来扫描输入并返回一个表示输入字符串中找到的词汇的String。

这是我到目前为止所得到的,但我不知道我是否朝着正确的方向前进 - 所有的帮助将不胜感激:)

private String scanNumbers(char input)
{
   String result= "";
   int value = in.read()
   if(value != -1)
   {
      If(isDigit(input))
       {
         result = Integer.toString(value);
        }
   }
 return result;
}

public static boolean isDigit(char input)
{
    return (input >= '0' && input <= '9');
}

谢谢,我是解析/ lexemes /编译器的新手。

1 个答案:

答案 0 :(得分:0)

简介

Questions that appear to be related to a homework exercise are often slow to be answered on SO。我们经常等到截止日期过后!

你提到你是解析/词法/编译器主题的新手,并且想要一些帮助来编写Java方法来扫描输入并返回表示输入字符串中找到的词汇的字符串。稍后你会澄清,表明你想要一个跳过字符直到找到数字的方法。

你的问题中存在很多混乱,导致你想要实现的冲突。

目前尚不清楚您是否想要了解如何在Java中进行词法分析作为更大的编译器项目的一部分,无论您是只想使用数字,是否要查找现有的工具或方法来执行此操作或正试图学习如何自己编程这些方法。如果你正在编程,你是否只需要知道阅读一个数字,或者这只是你想要做的事情的一个例子。

词法分析

词法分析,也称为扫描,是读取由字符组成的文本语料库的过程。这可以用于多种目的,例如数据输入,书面材料的语言分析(例如词频计数)或语言编译或解释的一部分。当作为编译的一部分完成时,它是一系列阶段(通常是第一阶段),包括解析,语义分析,代码生成,优化等。在编写编译器code generator tools时,通常使用它,因此如果需要用Java编写编译器,那么通常会使用Java词法生成器和Java解析器生成器来为这些编译器组件创建Java代码。有时lexer和解析器是手写的,但对于新手来说它不是推荐的任务。它需要编译器编写专家来手动构建编译器,而不是工具集。有时候,作为课堂练习,要求学生编写代码来执行一个词汇分析,以帮助他们理解这个过程,但这通常是针对一些词汇,比如你的数字练习。

术语 lexeme 用于描述组成由词法分析器识别的单个实体的一系列字符。一旦识别,它通常由令牌表示。因此,词汇被词干替换为词汇分析过程的一部分。词法分析器有时会将符号记录在符号表中以供以后使用,然后再用令牌替换它。这就是程序中的标识符经常记录在编译器中的方式。

有几种用于在Java中构建词法分析器的工具。其中两个最常见的是JlexJFlex。为了说明它们如何工作,在跳过空格时识别整数,我们将使用following rules

%%
WHITE_SPACE_CHAR=[\n\ \t\b\012]
DIGIT=[0-9]
%%
{WHITE_SPACE_CHAR}+  { }
{DIGIT}+   { return(new Yytoken(42,yytext(),yyline,yychar,yychar + yytext().length())); }
%%

将由工具处理以生成Java方法以实现该任务。

用于描述词汇的符号通常写为regular expressions。计算机科学理论可以帮助我们编写词法分析器。正则表达式可以用finite state automata的形式表示。有一个特定的style of coding that can be used to match lexemes that experienced programers would recognise and use in this situation,它涉及一个循环内的开关:

while ( ! eof ) {
  switch ( next_symbol() ) {

  case symbol:
      ...
  break;
  default:
        error(diagnostic); break;
  }
 }

这些概念通常是一个简单的词汇编程练习,旨在向学生介绍。

Java中的标记

通过所有这些初步解释,让我们来看看你的Java代码。正如评论中所提到的,Java在从input stream and reading characters读取字节之间存在差异,因为字符在unicode中,由两个字节表示。您已在字符处理方法中使用了字节读取。

识别输入流中的简单标记,特别是数据输入,是Java具有specific built-in class for that called the StreamTokenizer的常见活动。

我们可以通过以下方式实现您的任务,例如:

    // create a new tokenizer
     Reader r = new BufferedReader(new InputStreamReader( System.in ));
     StreamTokenizer st = new StreamTokenizer(r);

     // print the stream tokens
     boolean eof = false;
     do {

        int token = st.nextToken();
        switch (token) {
           case StreamTokenizer.TT_EOF:
              System.out.println("End of File encountered.");
              eof = true;
              break;
           case StreamTokenizer.TT_EOL:
              System.out.println("End of Line encountered.");
              break;
           case StreamTokenizer.TT_NUMBER:
              System.out.println("Number: " + st.nval);
              break;
           default:
              System.out.println((char) token + " encountered.");
              if (token == '!') {
                 eof = true;
              }
        }
     } while (!eof);

但是,这不会返回数字的lexeme字符串,只匹配数字并获取值。

我发现您注意到了Java class java.util.scanner,因为您的问题已将其作为标记。这是另一个可以执行模拟操作的类。 我们可以从输入中得到一个整数lexeme,如下所示:

Scanner s = new Scanner(System.in);
System.out.println(s.nextInt());

解决方案

最后,让我们重新编写原始代码,找到跳过不需要的字符的整数的词位,我使用java regular expression matching

import java.io.IOException;    import java.io.InputStreamReader;
import java.util.regex.Pattern;
public class ReadNumbers {
    static InputStreamReader in = null;            // Have input source as a global
    static int value = -1;                         // and the current input value       
    public static void main ( String [] args ) {
        try {
            in = new InputStreamReader(System.in); // Set up the input
            value = in.read();                     // pre-fill the input state              
            System.out.println(scanNumbers()) ;               
        }
        catch (Exception e) {
           e.printStackTrace();            // print error
        } 
    }
    private static String scanNumbers() {
        String SkipCharacters = "\\s" ;           // Characters that can be skipped
        String result= "";                        // empty string to store lexeme
        int charcount=0;
        try {
            while ( (value != -1) && Pattern.matches(SkipCharacters,"" + (char)value) ) 
                // Now skip optional characters before the number
                value = in.read() ;               // pre-load the next character
            while ( (value != -1) && isDigit((char)value)) { 
               // Now find the number digits
               result = result + (char)value;    // append digit character to result
               value = in.read() ;               // pre-load the next character
            }
        } finally {
           return result;
        }
    }
    public static boolean isDigit(char input) {
        return (input >= '0' && input <= '9');
    }
}

后记

来自@markspace的评论非常有趣且有用,因为它指出并非所有数字都是小数字。 考虑其他基数中的数字,如十六进制。 Java allows integer constants to be specified in those number bases which do not just use the digits 0..9