JavaRush /Java-Blog /Random-DE /Reguläre Ausdrücke in Java, Teil 5

Reguläre Ausdrücke in Java, Teil 5

Veröffentlicht in der Gruppe Random-DE
Wir präsentieren Ihnen eine Übersetzung einer kurzen Anleitung zu regulären Ausdrücken in Java, die Jeff Friesen für die Javaworld-Website geschrieben hat. Zur besseren Lesbarkeit haben wir den Artikel in mehrere Teile gegliedert. Dieser Teil ist der letzte. Reguläre Ausdrücke in Java, Teil 5 - 1Reguläre Ausdrücke in Java, Teil 1 Reguläre Ausdrücke in Java, Teil 2 Reguläre Ausdrücke in Java, Teil 3 Reguläre Ausdrücke in Java, Teil 4

Verwendung regulärer Ausdrücke für die lexikalische Analyse

Eine noch nützlichere Anwendung regulärer Ausdrücke ist eine Bibliothek mit wiederverwendbarem Code zur Durchführung lexikalischer Analysen, einer Schlüsselkomponente jedes Compilers oder Assemblers. In diesem Fall wird der Eingabestrom von Zeichen in einen Ausgabestrom von Token gruppiert – Namen für Zeichenfolgen, die eine gemeinsame Bedeutung haben. Wenn der lexikalische Analysator beispielsweise auf die Zeichenfolge c, o, u, n, t, e, r, im Eingabestream stößt, kann er ein Token ID(Identifikator) ausgeben. Die dem Token entsprechende Zeichenfolge wird als Lexem bezeichnet.
Mehr über Marker und Lexeme
Token wie ID können mit vielen Zeichenfolgen übereinstimmen. Im Fall solcher Token wird das tatsächliche Token, das dem Token entspricht, auch vom Compiler, Assembler oder einem anderen Dienstprogramm benötigt, das eine lexikalische Analyse erfordert. Für Token, die eine bestimmte Zeichenfolge darstellen, wie z. B. das Token, das PLUSnur dem Zeichen entspricht +, ist das tatsächliche Token nicht erforderlich, da es durch das Token [eindeutig] bestimmt werden kann.
Reguläre Ausdrücke sind viel effizienter als zustandsbasierte lexikalische Analysatoren, die von Hand geschrieben werden müssen und im Allgemeinen nicht wiederverwendet werden können. Ein Beispiel für einen auf regulären Ausdrücken basierenden lexikalischen Analysator ist JLex , ein lexikalischer Generator für die Java-Sprache, der reguläre Ausdrücke verwendet, um Regeln für die Aufteilung eines Eingabedatenstroms in Token zu definieren. Ein weiteres Beispiel ist Lexan.

Lernen Sie Lexan kennen

Lexan ist eine wiederverwendbare Java-Bibliothek für die lexikalische Analyse. Es basiert auf Code aus der Blog-Beitragsreihe Writing a Parser in Java auf der Cogito Learning- Website . Die Bibliothek besteht aus den folgenden Klassen, die im Paket ca.javajeff.lexanenthalten sind, das im herunterladbaren Code für diesen Artikel enthalten ist:
  • Lexan: lexikalischer Analysator;
  • LexanException: Im Klassenkonstruktor ausgelöste AusnahmeLexan;
  • LexException: Ausnahme wird ausgelöst, wenn bei der lexikalischen Analyse eine falsche Syntax erkannt wird;
  • Token: Name mit einem regulären Ausdrucksattribut;
  • TokLex: Token/Token-Paar.
Der Konstruktor Lexan(java.lang.Class tokensClass)erstellt einen neuen lexikalischen Analysator. Es erfordert ein Argument in Form eines Klassenobjekts, das java.lang.Classder Typkonstante class entspricht static Token. Mithilfe der Reflection-API liest der Konstruktor alle Konstanten Tokenin ein Wertearray ein Token[]. Wenn Tokenkeine Konstanten vorhanden sind, wird eine Ausnahme ausgelöst LexanException. Reguläre Ausdrücke in Java, Teil 5 - 2Die Klasse Lexanstellt außerdem die folgenden zwei Methoden bereit:
  • Die Methode gibt eine Liste dieses Lexers zurück;List getTokLexes() Token
  • Метод void lex(String str)führt eine lexikalische Analyse der Eingabezeichenfolge durch [mit Platzierung des Ergebnisses] in einer Liste von Werten vom Typ TokLex. Wenn ein Zeichen gefunden wird, das keinem der Array-Muster entspricht Token[], wird eine Ausnahme ausgelöst LexException.
Die Klasse LexanExceptionverfügt über keine Methoden; sie verwendet eine geerbte Methode, um eine Ausnahmemeldung zurückzugeben getMessage(). Im Gegensatz dazu stellt die Klasse LexExceptiondie folgenden Methoden bereit:
  • Die Methode int getBadCharIndex()gibt die Position eines Zeichens zurück, das keinem der Markierungsmuster entspricht.
  • Die Methode String getText()gibt den Text zurück, der analysiert wurde, als die Ausnahme generiert wurde.
Die Klasse Tokenüberschreibt die Methode toString(), um den Namen des Markers zurückzugeben. Es stellt außerdem eine Methode bereit String getPattern(), die das reguläre Ausdrucksattribut des Tokens zurückgibt. Die Klasse TokLexstellt eine Methode bereit Token getToken(), die ihr Token zurückgibt. Es stellt auch eine Methode bereit String getLexeme(), die ihr Token zurückgibt.

Demonstration der Lexan-Bibliothek

Um zu demonstrieren, wie die Bibliothek funktioniert, Lexanhabe ich eine Anwendung geschrieben LexanDemo. Es besteht aus den Klassen LexanDemo, und . Der Quellcode für die Anwendung ist in Listing 2 dargestellt. Listing 2. Demonstration der Lexan-Bibliothek in AktionBinTokensMathTokensNoTokensLexanDemo
import ca.javajeff.lexan.Lexan;
import ca.javajeff.lexan.LexanException;
import ca.javajeff.lexan.LexException;
import ca.javajeff.lexan.TokLex;

public final class LexanDemo
{
   public static void main(String[] args)
   {
      lex(MathTokens.class, " sin(x) * (1 + var_12) ");
      lex(BinTokens.class, " 1 0 1 0 1");
      lex(BinTokens.class, "110");
      lex(BinTokens.class, "1 20");
      lex(NoTokens.class, "");
   }

   private static void lex(Class tokensClass, String text)
   {
      try
      {
         Lexan lexan = new Lexan(tokensClass);
         lexan.lex(text);
         for (TokLex tokLex: lexan.getTokLexes())
            System.out.printf("%s: %s%n", tokLex.getToken(),
                              tokLex.getLexeme());
      }
      catch (LexanException le)
      {
         System.err.println(le.getMessage());
      }
      catch (LexException le)
      {
         System.err.println(le.getText());
         for (int i = 0; i < le.getBadCharIndex(); i++)
            System.err.print("-");
         System.err.println("^");
         System.err.println(le.getMessage());
      }
      System.out.println();
   }
}
Die Methode main()in Listing 2 ruft ein Dienstprogramm auf lex(), um die lexikalische Analyse mit Lexan zu demonstrieren. Bei jedem Aufruf dieser Methode werden die Klasse der Token im Objekt Classund die zu analysierende Zeichenfolge übergeben. Die Methode lex()erstellt zunächst ein Objekt der Klasse Lexan, indem sie das Objekt Classan den Klassenkonstruktor übergibt Lexan. Und dann ruft es die lex()Klassenmethode Lexanfür diese Zeichenfolge auf. Wenn die lexikalische Analyse erfolgreich ist, wird die Klassenmethode TokLexaufgerufen , um eine Liste von Objekten zurückzugeben . Für jedes dieser Objekte wird seine Klassenmethode aufgerufen, um das Token zurückzugeben, und seine Klassenmethode, um das Token zurückzugeben. Beide Werte werden auf der Standardausgabe gedruckt. Wenn die lexikalische Analyse fehlschlägt, wird eine der Ausnahmen oder ausgelöst und entsprechend behandelt . Betrachten wir der Kürze halber nur die Klasse, aus der diese Anwendung besteht . Listing 3 zeigt seinen Quellcode. Listing 3. Beschreibung einer Reihe von Token für eine kleine mathematische SprachegetTokLexes()LexangetToken()TokLexgetLexeme()LexanExceptionLexExceptionMathTokens
import ca.javajeff.lexan.Token;

public final class MathTokens
{
   public final static Token FUNC = new Token("FUNC", "sin|cos|exp|ln|sqrt");
   public final static Token LPAREN = new Token("LPAREN", "\\(");
   public final static Token RPAREN = new Token("RPAREN", "\\)");
   public final static Token PLUSMIN = new Token("PLUSMIN", "[+-]");
   public final static Token TIMESDIV = new Token("TIMESDIV", "[*/]");
   public final static Token CARET = new Token("CARET", "\\^");
   public final static Token INTEGER = new Token("INTEGER", "[0-9]+");
   public final static Token ID = new Token("ID", "[a-zA-Z][a-zA-Z0-9_]*");
}
Listing 3 zeigt, dass die Klasse MathTokenseine Folge von Konstanten vom Typ beschreibt Token. Jedem von ihnen wird der Wert eines Objekts zugewiesen Token. Der Konstruktor für dieses Objekt empfängt eine Zeichenfolge mit dem Namen der Markierung sowie einen regulären Ausdruck, der alle mit dieser Markierung verknüpften Zeichenfolgen beschreibt. Aus Gründen der Übersichtlichkeit ist es wünschenswert, dass der Stringname des Markers mit dem Namen der Konstante übereinstimmt, dies ist jedoch nicht erforderlich. Reguläre Ausdrücke in Java, Teil 5 - 3Die Position der Konstante Tokenin der Liste der Marker ist wichtig. Konstanten, die sich weiter oben in der Liste befinden, Tokenhaben Vorrang vor den Konstanten, die sich darunter befinden. Wenn Lexan beispielsweise auf trifft sin, wählt er den Token FUNCanstelle von ID. Wenn die Markierung IDvor der Markierung gestanden hätte FUNC, wäre sie ausgewählt worden.

Kompilieren und Ausführen der LexanDemo-Anwendung

Der herunterladbare Code für diesen Artikel enthält ein Archiv lexan.zipmit allen Dateien der Lexan-Distribution. Entpacken Sie dieses Archiv und gehen Sie in ein Unterverzeichnis demosdes Stammverzeichnisses lexan. Wenn Sie Windows verwenden, führen Sie den folgenden Befehl aus, um die Quellcodedateien der Demoanwendung zu kompilieren:
javac -cp ..\library\lexan.jar *.java
Wenn die Kompilierung erfolgreich war, führen Sie den folgenden Befehl aus, um die Demoanwendung auszuführen:
java -cp ..\library\lexan.jar;. LexanDemo
Sie sollten die folgenden Ergebnisse sehen:
FUNC: sin
LPAREN: (
ID: x
RPAREN: )
TIMESDIV: *
LPAREN: (
INTEGER: 1
PLUSMIN: +
ID: var_12
RPAREN: )
ONE: 1
ZERO: 0
ONE: 1
ZERO: 0
ONE: 1
ONE: 1
ONE: 1
ZERO: 0
1 20
--^
Неожиданный символ во входном тексте: 20
Die Meldung Неожиданный символ во входном тексте: 20tritt auf, wenn eine Ausnahme ausgelöst wird, LexanExceptionda die Klasse BinTokenskeine Konstante Tokenmit einem Wert 2als regulären Ausdruck deklariert. Beachten Sie, dass der Ausnahmebehandler die Position des unpassenden Zeichens ausgibt, die aus der lexikalischen Analyse des Textes ermittelt wurde. Die Meldung „Tokens fehlen“ ist das Ergebnis einer ausgelösten Ausnahme, LexExceptionda in der Klasse NoTokenskeine Konstanten deklariert sind Token.

Hinter den Kulissen

Lexanverwendet die Lexan-Klasse als Engine. Schauen Sie sich die Implementierung dieser Klasse in Listing 4 an und beachten Sie den Beitrag regulärer Ausdrücke zur Wiederverwendbarkeit der Engine. Listing 4. Erstellen einer lexikalischen Analysearchitektur basierend auf regulären Ausdrücken
package ca.javajeff.lexan;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;

/**
 *  Лексический анализатор. Этот класс можно использовать для
 *  преобразования входного потока символов в выходной поток маркеров.
 *
 *  @Autor Джефф Фризен
 */

public final class Lexan
{
   private List tokLexes;

   private Token[] values;

   /**
    *  Инициализируем лексический анализатор набором ein Objektов Token.
    *
    *  @параметры tokensClass – ein Objekt Class класса, содержащего
    *       набор ein Objektов Token
    *
    *  @генерирует исключение LexanException в случае невозможности
    *       формирования ein Objektа Lexan, возможно, из-за отсутствия ein Objektов
    *       Token в классе
    */

   public Lexan(Class tokensClass) throws LexanException
   {
      try
      {
         tokLexes = new ArrayList<>();
         List _values = new ArrayList<>();
         Field[] fields = tokensClass.getDeclaredFields();
         for (Field field: fields)
            if (field.getType().getName().equals("ca.javajeff.lexan.Token"))
               _values.add((Token) field.get(null));
         values = _values.toArray(new Token[0]);
         if (values.length == 0)
            throw new LexanException("маркеры отсутствуют");
      }
      catch (IllegalAccessException iae)
      {
         throw new LexanException(iae.getMessage());
      }

   /**
    * Получаем список TokLex'ов этого лексического анализатора.
    *
    *  @возвращает список TokLex'ов
    */

   public List getTokLexes()
   {
      return tokLexes;
   }

   /** * Выполняет лексический анализ входной строки [с помещением * результата] в список TokLex'ов. * * @параметры str – строка, подвергаемая лексическому анализу * * @генерирует исключение LexException: во входных данных обнаружен * неожиданный символ */

   public void lex(String str) throws LexException
   {
      String s = new String(str).trim(); // удалить ведущие пробелы
      int index = (str.length() - s.length());
      tokLexes.clear();
      while (!s.equals(""))
      {
         boolean match = false;
         for (int i = 0; i < values.length; i++)
         {
            Token token = values[i];
            Matcher m = token.getPattern().matcher(s);
            if (m.find())
            {
               match = true;
               tokLexes.add(new TokLex(token, m.group().trim()));
               String t = s;
               s = m.replaceFirst("").trim(); // удалить ведущие пробелы
               index += (t.length() - s.length());
               break;
            }
         }
         if (!match)
            throw new LexException("Неожиданный символ во входном тексте: "
                                    + s, str, index);
      }
   }
}
Der Methodencode lex()basiert auf dem Code, der im Blogbeitrag „Writing a Parser in Java: A Token Generator“ auf der Cogito Learning-Website bereitgestellt wird. Lesen Sie diesen Beitrag, um mehr darüber zu erfahren, wie Lexan die Regex-API zum Kompilieren von Code verwendet. Reguläre Ausdrücke in Java, Teil 5 - 4

Abschluss

Reguläre Ausdrücke sind ein nützliches Werkzeug, das für jeden Entwickler nützlich sein kann. Die Regex-API der Programmiersprache Java erleichtert die Verwendung in Anwendungen und Bibliotheken. Nachdem Sie nun bereits über ein grundlegendes Verständnis von regulären Ausdrücken und dieser API verfügen, werfen Sie einen Blick auf die SDK-Dokumentation, java.util.regexum noch mehr über reguläre Ausdrücke und zusätzliche Regex-API-Methoden zu erfahren.
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION