JavaRush /Java Blog /Random-IT /Espressioni regolari in Java, parte 5

Espressioni regolari in Java, parte 5

Pubblicato nel gruppo Random-IT
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 5 - 1Espressioni regolari in Java, Parte 1 Espressioni regolari in Java, Parte 2 Espressioni regolari in Java, Parte 3 Espressioni regolari in Java, Parte 4

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 caratteri c, 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 PLUScorrispondente solo al carattere +, il token effettivo non è richiesto poiché può essere determinato [univocamente] dal token.
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.

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 pacchetto ca.javajeff.lexanincluso nel codice scaricabile per questo articolo:
  • Lexan: analizzatore lessicale;
  • LexanException: eccezione lanciata nel costruttore della classeLexan;
  • 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.
Il costruttore Lexan(java.lang.Class tokensClass)crea un nuovo analizzatore lessicale. Richiede un argomento sotto forma di un oggetto di classe java.lang.Classcorrispondente al tipo costante class static Token. Utilizzando l'API Reflection, il costruttore legge tutte le costanti Tokenin un array di valori Token[]. Se Tokennon sono presenti costanti, viene lanciata un'eccezione LexanException. Espressioni regolari in Java, parte 5 - 2La classe Lexanfornisce 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 tipo TokLex. Se viene incontrato un carattere che non corrisponde a nessuno dei modelli dell'array Token[], viene generata un'eccezione LexException.
La classe LexanExceptionnon ha metodi; utilizza un metodo ereditato per restituire un messaggio di eccezione getMessage(). Al contrario, la classe LexExceptionfornisce 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.
La classe Tokensovrascrive 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 TokLexfornisce 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, Lexanho scritto un'applicazione LexanDemo. È composto dalle classi LexanDemo, BinTokens, MathTokense 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 Classe la stringa da analizzare. Il metodo lex()crea innanzitutto un oggetto della classe Lexanpassando l'oggetto Classal costruttore della classe Lexan. E poi chiama il metodo lex()della classe Lexansu quella stringa. Se l'analisi lessicale ha esito positivo, TokLexviene 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 TokLexper restituire il token e il relativo metodo di classe getLexeme()per restituire il token. Entrambi i valori vengono stampati sull'output standard. LexanExceptionSe 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 MathTokensdescrive 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. Espressioni regolari in Java, parte 5 - 3La posizione della costante Tokennell'elenco dei marcatori è importante. Le costanti situate più in alto nell'elenco Tokenhanno la precedenza su quelle situate sotto. Ad esempio, quando incontra sin, Lexan sceglie il segnalino FUNCinvece di ID. Se il marcatore IDavesse preceduto il marcatore FUNC, sarebbe stato selezionato.

Compilazione ed esecuzione dell'applicazione LexanDemo

Il codice scaricabile per questo articolo include un archivio lexan.zipcontenente tutti i file della distribuzione Lexan. Scompatta questo archivio e vai in una sottodirectory demosdella 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 Неожиданный символ во входном тексте: 20si verifica come risultato di un'eccezione generata LexanExceptiona causa del fatto che la classe BinTokensnon dichiara una costante Tokencon un valore 2come 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 LexExceptionperché nella classe NoTokensnon sono dichiarate costanti Token.

Dietro le quinte

Lexanutilizza 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. Espressioni regolari in Java, parte 5 - 4

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'SDK java.util.regexper saperne di più sulle espressioni regolari e sui metodi aggiuntivi dell'API Regex.
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION