Java의 스캐너 클래스와 일반 표현식을 사용하여 입력을 토큰 화하려면 어떻게해야합니까?

StackOverflow https://stackoverflow.com/questions/244115

문제

내 자신의 목적을 위해, 나는 정기적 인 문법을 정의하고 그것을 기반으로 입력을 할 수있는 Java에 토큰 화기를 만들려고 노력하고 있습니다. StringTokenizer 클래스는 더 이상 사용되지 않았으며 스캐너에서 내가하고 싶은 일에 대한 힌트를주는 몇 가지 기능을 발견했지만 아직 운이 없습니다. 누구든지 이것에 대해 좋은 방법을 알고 있습니까?

도움이 되었습니까?

해결책

"스캐너"라는 이름은 약간 오해의 소지가 있습니다. 단어는 종종 어휘 분석기를 의미하는 데 사용되기 때문입니다. 그것은 모든 것이 대체입니다 scanf() C, Perl, et al. StringTokenizer와 같은 split(), 주어진 패턴에 대한 일치를 찾을 때까지 미리 스캔하도록 설계되었으며, 도중에 건너 뛰는 것은 토큰으로 반환됩니다.

반면 어휘 분석기는 안전하게 무시할 수 있는지 여부를 결정하는 경우에도 모든 캐릭터를 검사하고 분류해야합니다. 즉, 각 경기 후에는 일치하는 패턴을 찾을 때까지 여러 패턴을 적용 할 수 있습니다. 그 시점에서 시작합니다. 그렇지 않으면, 그것은 "//"시퀀스를 찾을 수 있으며, 그것이 문자열 문자 내부에 있고 오프닝 견적 마크를 알아 차리지 못했을 때 주석의 시작을 발견했다고 생각할 수 있습니다.

물론 실제로는 그보다 훨씬 더 복잡하지만 StringTokenizer와 같은 내장 도구가 이유를 보여줍니다. split() 그리고 스캐너는 이런 종류의 작업에 적합하지 않습니다. 그러나 제한된 형태의 어휘 분석을 위해 Java의 Regex 클래스를 사용하는 것이 가능합니다. 실제로, 스캐너 클래스를 추가하면 새 대사가 API를 지원하기 위해 추가 된 새 대사관 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() 방법. :디

다른 팁

질문을 잘 이해하면 문자열을 토큰 화하는 두 가지 예가 있습니다. 스캐너 클래스가 필요하지 않으며, 토큰을 사전 캐스트하거나 배열을 사용하는 것보다 더 소프통적으로 반복하려는 경우에만 스캐너 클래스가 필요하지 않습니다. 배열이 충분한 경우 아래에 주어진대로 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);
        }
    }

}

이것이 간단한 프로젝트를위한 것이라면 (일이 어떻게 작동하는지 배우기 위해) Balint Pato가 말한 것과 함께 가십시오.

이것이 더 큰 프로젝트를위한 경우 스캐너 생성기를 사용하는 것을 고려하십시오. JFLEX 대신에. 다소 복잡하지만 더 빠르고 강력합니다.

여기에있는 대부분의 답변은 이미 훌륭하지만 지적하지 않으면 나는 해고 될 것입니다. antlr. 이 우수한 도구 주위에 전체 컴파일러를 만들었습니다. 버전 3에는 몇 가지 놀라운 기능이 있으며 잘 정의 된 문법을 기반으로 입력을 구문 분석 해야하는 모든 프로젝트에 권장합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top