Presentiamo alla vostra attenzione la traduzione di una breve guida alle espressioni regolari in Java, scritta da Jeff Friesen per il sito javaworld. Per facilitare la lettura abbiamo diviso l’articolo in più parti. Questa parte è quella finale. Espressioni regolari in Java, Parte 1 Espressioni regolari in Java, Parte 2 Espressioni regolari in Java, Parte 3 Espressioni regolari in Java, Parte 4
Le espressioni regolari sono molto più efficienti degli analizzatori lessicali basati sullo stato, che devono essere scritti a mano e generalmente non possono essere riutilizzati. Un esempio di analizzatore lessicale basato su espressioni regolari è JLex , un generatore lessicale per il linguaggio Java che utilizza espressioni regolari per definire regole per suddividere un flusso di dati di input in token. Un altro esempio è Lexan.
Utilizzo delle espressioni regolari per l'analisi lessicale
Un'applicazione ancora più utile delle espressioni regolari è una libreria di codice riutilizzabile per eseguire analisi lessicali, un componente chiave di qualsiasi compilatore o assemblatore. In questo caso, il flusso di caratteri in input viene raggruppato in un flusso di token in uscita: nomi per sequenze di caratteri che hanno un significato comune. Ad esempio, dopo essersi imbattuto nella sequenza di caratteric
, o
, u
, n
, t
, e
, r
, nel flusso di input, l'analizzatore lessicale può emettere un token ID
(identificatore). La sequenza di caratteri corrispondente al token è chiamata lessema.
Maggiori informazioni su marcatori e lessemi |
---|
I token come ID possono corrispondere a molte sequenze di caratteri. Nel caso di tali token, il token effettivo corrispondente al token è necessario anche al compilatore, all'assemblatore o ad altra utilità che richiede l'analisi lessicale. Per i token che rappresentano una sequenza specifica di caratteri, come il token PLUS corrispondente solo al carattere + , il token effettivo non è richiesto poiché può essere determinato [univocamente] dal token. |
Conoscere Lexan
Lexan è una libreria Java riutilizzabile per l'analisi lessicale. Si basa sul codice della serie di post di blog Writing a Parser in Java sul sito Web Cogito Learning . La libreria è composta dalle seguenti classi, presenti nel pacchettoca.javajeff.lexan
incluso nel codice scaricabile per questo articolo:
Lexan
: analizzatore lessicale;LexException
: eccezione lanciata se viene rilevata una sintassi errata durante l'analisi lessicale;Token
: nome con un attributo di espressione regolare;TokLex
: coppia gettone/gettone.
LexanException
: eccezione lanciata nel costruttore della classeLexan;
Lexan(java.lang.Class tokensClass)
crea un nuovo analizzatore lessicale. Richiede un argomento sotto forma di un oggetto di classe java.lang.Class
corrispondente al tipo costante class static Token
. Utilizzando l'API Reflection, il costruttore legge tutte le costanti Token
in un array di valori Token[]
. Se Token
non sono presenti costanti, viene lanciata un'eccezione LexanException
. La classe Lexan
fornisce inoltre i due metodi seguenti:
- Il metodo restituisce un elenco di questo lexer;
List
getTokLexes() Token
Метод void lex(String str)
esegue un'analisi lessicale della stringa di input [con il risultato inserito] in una lista di valori di tipoTokLex
. Se viene incontrato un carattere che non corrisponde a nessuno dei modelli dell'arrayToken[]
, viene generata un'eccezioneLexException
.
LexanException
non ha metodi; utilizza un metodo ereditato per restituire un messaggio di eccezione getMessage()
. Al contrario, la classe LexException
fornisce i seguenti metodi:
- Il metodo
int getBadCharIndex()
restituisce la posizione di un carattere che non corrisponde a nessuno dei modelli di marcatori. - Il metodo
String getText()
restituisce il testo analizzato al momento della generazione dell'eccezione.
Token
sovrascrive il metodo toString()
per restituire il nome del marcatore. Fornisce inoltre un metodo String getPattern()
che restituisce l'attributo dell'espressione regolare del token. La classe TokLex
fornisce un metodo Token getToken()
che restituisce il suo token. Fornisce inoltre un metodo String getLexeme()
che restituisce il relativo token.
Dimostrazione della libreria Lexan
Per dimostrare come funziona la libreria,Lexan
ho scritto un'applicazione LexanDemo
. È composto dalle classi LexanDemo
, BinTokens
, MathTokens
e NoTokens
. Il codice sorgente dell'applicazione LexanDemo
è mostrato nel Listato 2. Listato 2. Dimostrazione della libreria Lexan in azione
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();
}
}
Il metodo main()
nel Listato 2 richiama un'utilità lex()
per dimostrare l'analisi lessicale utilizzando Lexan. Ad ogni chiamata a questo metodo viene passata la classe dei token nell'oggetto Class
e la stringa da analizzare. Il metodo lex()
crea innanzitutto un oggetto della classe Lexan
passando l'oggetto Class
al costruttore della classe Lexan
. E poi chiama il metodo lex()
della classe Lexan
su quella stringa. Se l'analisi lessicale ha esito positivo, TokLex
viene chiamato il metodo getTokLexes()
della classe per restituire un elenco di oggetti Lexan
. Per ciascuno di questi oggetti, viene chiamato il relativo metodo getToken()
di classe TokLex
per restituire il token e il relativo metodo di classe getLexeme()
per restituire il token. Entrambi i valori vengono stampati sull'output standard. LexanException
Se l'analisi lessicale fallisce, viene generata una delle eccezioni o gestita di conseguenza LexException
. Per brevità consideriamo solo la classe che compone questa applicazione MathTokens
. Il Listato 3 mostra il suo codice sorgente. Listato 3. Descrizione di un insieme di token per un piccolo linguaggio matematico
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_]*");
}
Il Listato 3 mostra che la classe MathTokens
descrive una sequenza di costanti di tipo Token
. A ciascuno di essi viene assegnato il valore di un oggetto Token
. Il costruttore di questo oggetto riceve una stringa che rappresenta il nome del marcatore, insieme a un'espressione regolare che descrive tutte le stringhe di caratteri associate a quel marcatore. Per chiarezza, è auspicabile che il nome della stringa del marcatore sia lo stesso del nome della costante, ma ciò non è obbligatorio. La posizione della costante Token
nell'elenco dei marcatori è importante. Le costanti situate più in alto nell'elenco Token
hanno la precedenza su quelle situate sotto. Ad esempio, quando incontra sin
, Lexan sceglie il segnalino FUNC
invece di ID
. Se il marcatore ID
avesse preceduto il marcatore FUNC
, sarebbe stato selezionato.
Compilazione ed esecuzione dell'applicazione LexanDemo
Il codice scaricabile per questo articolo include un archiviolexan.zip
contenente tutti i file della distribuzione Lexan. Scompatta questo archivio e vai in una sottodirectory demos
della directory root lexan
. Se utilizzi Windows, esegui il comando seguente per compilare i file del codice sorgente dell'applicazione demo:
javac -cp ..\library\lexan.jar *.java
Se la compilazione ha esito positivo, eseguire il comando seguente per eseguire l'applicazione demo:
java -cp ..\library\lexan.jar;. LexanDemo
Dovresti vedere i seguenti risultati:
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
Il messaggio Неожиданный символ во входном тексте: 20
si verifica come risultato di un'eccezione generata LexanException
a causa del fatto che la classe BinTokens
non dichiara una costante Token
con un valore 2
come espressione regolare. Si noti che il gestore delle eccezioni restituisce la posizione del carattere inappropriato ottenuto dall'analisi lessicale del testo. Il messaggio relativo ai token mancanti è il risultato di un'eccezione generata LexException
perché nella classe NoTokens
non sono dichiarate costanti Token
.
Dietro le quinte
Lexan
utilizza la classe Lexan come motore. Dai un'occhiata all'implementazione di questa classe nel Listato 4 e nota il contributo delle espressioni regolari nel rendere il motore riutilizzabile. Listato 4. Creazione di un'architettura di analisi lessicale basata su espressioni regolari
package ca.javajeff.lexan;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
/**
* Лексический анализатор. Этот класс можно использовать для
* преобразования входного потока символов в выходной поток маркеров.
*
* @Author Джефф Фризен
*/
public final class Lexan
{
private List tokLexes;
private Token[] values;
/**
* Инициализируем лексический анализатор набором an objectов Token.
*
* @параметры tokensClass – an object Class класса, содержащего
* набор an objectов Token
*
* @генерирует исключение LexanException в случае невозможности
* формирования an object Lexan, возможно, из-за отсутствия an objectов
* 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);
}
}
}
Il codice del metodo lex()
si basa sul codice fornito nel post del blog "Writing a Parser in Java: A Token Generator" sul sito Web Cogito Learning. Leggi questo post per saperne di più su come Lexan utilizza l'API Regex per compilare il codice.
Conclusione
Le espressioni regolari sono uno strumento utile che può essere utile a qualsiasi sviluppatore. L'API Regex del linguaggio di programmazione Java li rende facili da usare in applicazioni e librerie. Ora che hai già una conoscenza di base delle espressioni regolari e di questa API, dai un'occhiata alla documentazione dell'SDKjava.util.regex
per saperne di più sulle espressioni regolari e sui metodi aggiuntivi dell'API Regex.
GO TO FULL VERSION