Frage

Nur für meine eigenen Zwecke, Ich versuche, eine tokenizer in Java zu bauen, wo ich eine reguläre Grammatik definieren und haben es Eingang tokenize auf dieser Grundlage. Die StringTokenizer Klasse ist veraltet, und ich habe ein paar Funktionen in Scanner gefunden, die in Richtung andeuten, was ich tun will, aber noch kein Glück. Wer weiß, einen guten Weg, um darüber zu gehen?

War es hilfreich?

Lösung

Der Name „Scanner“ ist ein wenig irreführend, weil das Wort oft bedeutete einen lexikalischen Analysator verwendet wird, und das ist nicht das, was Scanner für ist. es ist alles ist ein Ersatz für die scanf() Funktion, die Sie in C finden, Perl, et al . Wie StringTokenizer und split(), speziell voraus zu scannen, bis es eine Übereinstimmung für ein bestimmtes Muster findet, und was er übersprungen auf dem Weg über als Token zurückgegeben.

Ein Lexer, auf der anderen Seite, hat zu prüfen, und jedes Zeichen zu klassifizieren, auch wenn es nur zu entscheiden, ob es sie ignorieren. Das heißt, nach jedem Spiel, kann es mehrere Muster gelten, bis es eine findet, die passt an diesem Punkt beginnen . Andernfalls kann es die Sequenz „//“ finden und denkt, dass es den Beginn eines Kommentars gefunden wird, wenn es wirklich in einem Stringliteral und es nicht ganz das öffnendes Anführungszeichen bemerken.

Es ist eigentlich viel komplizierter als das, natürlich, aber ich bin nur illustriert, warum die integrierten Tools wie StringTokenizer, split() und Scanner für diese Art von Aufgabe nicht geeignet sind. Es ist jedoch möglich, die Java Regex-Klassen für eine begrenzte Form von lexikalischer Analyse zu verwenden. In der Tat machte die Zugabe der Klasse Scanner es viel einfacher, wegen des neuen Matcher API, die es zu unterstützen haben, das heißt, die Regionen und die usePattern() Methode. Hier ist ein Beispiel für einen rudimentären Scanner auf den Java Regex-Klassen gebaut.

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);
    }
  }
}

Das, nebenbei bemerkt, ist die einzige gute Verwendung ich je für die lookingAt() Methode gefunden habe. : D

Andere Tipps

Wenn ich verstehe Ihre Frage gut, dann sind hier zwei beispielhafte Verfahren eine Zeichenfolge tokenize. Sie haben nicht einmal die Klasse Scanner benötigen, nur, wenn Sie die Token vorab gegossen werden soll, oder durchlaufen sie mehr sofistically als ein Array verwendet wird. Wenn ein Array genug ist, benutzen Sie einfach String.split (), wie unten angegeben.

Bitte mehr Anforderungen gibt präzisere Antworten zu ermöglichen.

 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);
        }
    }

}

Wenn dies für ein einfaches Projekt (für das Lernen, wie die Dinge funktionieren), dann gehen Sie mit, was gesagt Balint Pato.

Wenn dies für ein größeres Projekt ist, sollten Sie einen Scanner Generator wie JFlex statt. Etwas komplizierter, aber schneller und leistungsfähig.

Die meisten Antworten sind hier schon sehr gut, aber ich wäre nachlässig, wenn ich nicht geklappt hat Punkt ANTLR . Ich habe, um dieses ausgezeichnete Tool gesamte Compiler erstellt. Version 3 hat einige erstaunliche Eigenschaften und ich würde es für jedes Projekt empfehlen die Eingabe analysieren erforderlich, basierend auf einer gut definierten Grammatik.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top