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 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
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.
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 Zeichenfolgec
, 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 PLUS nur dem Zeichen entspricht + , ist das tatsächliche Token nicht erforderlich, da es durch das Token [eindeutig] bestimmt werden kann. |
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 Paketca.javajeff.lexan
enthalten sind, das im herunterladbaren Code für diesen Artikel enthalten ist:
Lexan
: lexikalischer Analysator;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.
LexanException
: Im Klassenkonstruktor ausgelöste AusnahmeLexan;
Lexan(java.lang.Class tokensClass)
erstellt einen neuen lexikalischen Analysator. Es erfordert ein Argument in Form eines Klassenobjekts, das java.lang.Class
der Typkonstante class entspricht static Token
. Mithilfe der Reflection-API liest der Konstruktor alle Konstanten Token
in ein Wertearray ein Token[]
. Wenn Token
keine Konstanten vorhanden sind, wird eine Ausnahme ausgelöst LexanException
. Die Klasse Lexan
stellt 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 TypTokLex
. Wenn ein Zeichen gefunden wird, das keinem der Array-Muster entsprichtToken[]
, wird eine Ausnahme ausgelöstLexException
.
LexanException
verfügt über keine Methoden; sie verwendet eine geerbte Methode, um eine Ausnahmemeldung zurückzugeben getMessage()
. Im Gegensatz dazu stellt die Klasse LexException
die 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.
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 TokLex
stellt 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,Lexan
habe 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 AktionBinTokens
MathTokens
NoTokens
LexanDemo
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 Class
und die zu analysierende Zeichenfolge übergeben. Die Methode lex()
erstellt zunächst ein Objekt der Klasse Lexan
, indem sie das Objekt Class
an den Klassenkonstruktor übergibt Lexan
. Und dann ruft es die lex()
Klassenmethode Lexan
für diese Zeichenfolge auf. Wenn die lexikalische Analyse erfolgreich ist, wird die Klassenmethode TokLex
aufgerufen , 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()
Lexan
getToken()
TokLex
getLexeme()
LexanException
LexException
MathTokens
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 MathTokens
eine 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. Die Position der Konstante Token
in der Liste der Marker ist wichtig. Konstanten, die sich weiter oben in der Liste befinden, Token
haben Vorrang vor den Konstanten, die sich darunter befinden. Wenn Lexan beispielsweise auf trifft sin
, wählt er den Token FUNC
anstelle von ID
. Wenn die Markierung ID
vor 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 Archivlexan.zip
mit allen Dateien der Lexan-Distribution. Entpacken Sie dieses Archiv und gehen Sie in ein Unterverzeichnis demos
des 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 Неожиданный символ во входном тексте: 20
tritt auf, wenn eine Ausnahme ausgelöst wird, LexanException
da die Klasse BinTokens
keine Konstante Token
mit einem Wert 2
als 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, LexException
da in der Klasse NoTokens
keine Konstanten deklariert sind Token
.
Hinter den Kulissen
Lexan
verwendet 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.
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.regex
um noch mehr über reguläre Ausdrücke und zusätzliche Regex-API-Methoden zu erfahren.
GO TO FULL VERSION