JavaRush /Blog Java /Random-FR /Expressions régulières en Java, partie 5

Expressions régulières en Java, partie 5

Publié dans le groupe Random-FR
Nous présentons à votre attention la traduction d'un petit guide des expressions régulières en Java, rédigé par Jeff Friesen pour le site javaworld. Pour faciliter la lecture, nous avons divisé l'article en plusieurs parties. Cette partie est la dernière. Expressions régulières en Java, partie 5 - 1Expressions régulières en Java, partie 1 Expressions régulières en Java, partie 2 Expressions régulières en Java, partie 3 Expressions régulières en Java, partie 4

Utiliser des expressions régulières pour l'analyse lexicale

Une application encore plus utile des expressions régulières est une bibliothèque de code réutilisable pour effectuer une analyse lexicale, un composant clé de tout compilateur ou assembleur. Dans ce cas, le flux de caractères d'entrée est regroupé en un flux de sortie de jetons - noms de séquences de caractères ayant une signification commune. Par exemple, après être tombé sur la séquence de caractères c, o, u, n, t, e, r, dans le flux d'entrée, l'analyseur lexical peut générer un jeton ID(identifiant). La séquence de caractères correspondant au jeton est appelée un lexème.
En savoir plus sur les marqueurs et les lexèmes
Les jetons tels que l'ID peuvent correspondre à de nombreuses séquences de caractères. Dans le cas de tels jetons, le jeton réel correspondant au jeton est également nécessaire au compilateur, à l'assembleur ou à tout autre utilitaire nécessitant une analyse lexicale. Pour les jetons qui représentent une séquence spécifique de caractères, comme le jeton PLUScorrespondant uniquement au caractère +, le jeton réel n'est pas requis car il peut être déterminé [de manière unique] par le jeton.
Les expressions régulières sont beaucoup plus efficaces que les analyseurs lexicaux basés sur l'état, qui doivent être écrits à la main et ne peuvent généralement pas être réutilisés. Un exemple d'analyseur lexical basé sur des expressions régulières est JLex , un générateur lexical pour le langage Java qui utilise des expressions régulières pour définir des règles permettant de diviser un flux de données d'entrée en jetons. Un autre exemple est Lexan.

Apprendre à connaître Lexan

Lexan est une bibliothèque Java réutilisable pour l'analyse lexicale. Il est basé sur le code de la série d'articles de blog Writing a Parser in Java sur le site Web Cogito Learning . La bibliothèque se compose des classes suivantes, qui se trouvent dans le package ca.javajeff.lexaninclus dans le code téléchargeable de cet article :
  • Lexan: analyseur lexical ;
  • LexanException: exception levée dans le constructeur de classeLexan;
  • LexException: exception levée si une syntaxe incorrecte est détectée lors de l'analyse lexicale ;
  • Token: nom avec un attribut d'expression régulière ;
  • TokLex: paire jeton/jeton.
Le constructeur Lexan(java.lang.Class tokensClass)crée un nouvel analyseur lexical. Il nécessite un argument sous la forme d'un objet de classe java.lang.Classcorrespondant au type constant class static Token. À l'aide de l'API Reflection, le constructeur lit toutes les constantes Tokendans un tableau de valeurs Token[]. S'il Tokenn'y a pas de constantes, une exception est levée LexanException. Expressions régulières en Java, parties 5 - 2La classe Lexanfournit également les deux méthodes suivantes :
  • La méthode renvoie une liste de ce lexer ;List getTokLexes() Token
  • Метод void lex(String str)effectue une analyse lexicale de la chaîne d'entrée [avec le résultat placé] dans une liste de valeurs de type TokLex. Si un caractère rencontré ne correspond à aucun des modèles de tableau Token[], une exception est levée LexException.
La classe LexanExceptionn'a pas de méthodes ; elle utilise une méthode héritée pour renvoyer un message d'exception getMessage(). En revanche, la classe LexExceptionfournit les méthodes suivantes :
  • La méthode int getBadCharIndex()renvoie la position d'un caractère qui ne correspond à aucun des modèles de marqueur.
  • La méthode String getText()renvoie le texte analysé lors de la génération de l'exception.
La classe Tokenremplace la méthode toString()pour renvoyer le nom du marqueur. Il fournit également une méthode String getPattern()qui renvoie l'attribut d'expression régulière du jeton. La classe TokLexfournit une méthode Token getToken()qui renvoie son jeton. Il fournit également une méthode String getLexeme()qui renvoie son jeton.

Démonstration de la bibliothèque Lexan

Pour démontrer le fonctionnement de la bibliothèque, Lexanj'ai écrit une application LexanDemo. Il se compose de classes LexanDemo, BinTokenset MathTokens. NoTokensLe code source de l'application LexanDemoest présenté dans le Listing 2. Listing 2. Démonstration de la bibliothèque Lexan en action
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();
   }
}
La méthode main()du listing 2 appelle un utilitaire lex()pour démontrer l'analyse lexicale à l'aide de Lexan. Chaque appel à cette méthode reçoit la classe des jetons dans l'objet Classet la chaîne à analyser. La méthode lex()crée d'abord un objet de la classe Lexanen passant l'objet Classau constructeur de classe Lexan. Et puis il appelle la méthode lex()de classe Lexansur cette chaîne. Si l'analyse lexicale réussit, la méthode de classe TokLexest appelée pour renvoyer une liste d'objets . Pour chacun de ces objets, sa méthode de classe est appelée pour renvoyer le jeton et sa méthode de classe pour renvoyer le jeton. Les deux valeurs sont imprimées sur la sortie standard. Si l'analyse lexicale échoue, l'une des exceptions or est levée et traitée en conséquence . Par souci de concision, considérons uniquement la classe qui compose cette application . Le listing 3 montre son code source. Listing 3. Description d'un ensemble de jetons pour un petit langage mathématiquegetTokLexes()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_]*");
}
Le listing 3 montre que la classe MathTokensdécrit une séquence de constantes de type Token. Chacun d'eux se voit attribuer la valeur d'un objet Token. Le constructeur de cet objet reçoit une chaîne correspondant au nom du marqueur, ainsi qu'une expression régulière décrivant toutes les chaînes de caractères associées à ce marqueur. Pour plus de clarté, il est souhaitable que le nom de chaîne du marqueur soit le même que le nom de la constante, mais cela n'est pas obligatoire. Expressions régulières en Java, parties 5 à 3La position de la constante Tokendans la liste des marqueurs est importante. Les constantes situées plus haut dans la liste Tokenont priorité sur celles situées en dessous. Par exemple, lorsqu'il rencontre sin, Lexan choisit le jeton FUNCau lieu de ID. Si le marqueur IDavait précédé le marqueur FUNC, il aurait été sélectionné.

Compiler et exécuter l'application LexanDemo

Le code téléchargeable de cet article comprend une archive lexan.zipcontenant tous les fichiers de la distribution Lexan. Décompressez cette archive et accédez à un sous-répertoire demosdu répertoire racine lexan. Si vous utilisez Windows, exécutez la commande suivante pour compiler les fichiers de code source de l'application de démonstration :
javac -cp ..\library\lexan.jar *.java
Si la compilation réussit, exécutez la commande suivante pour exécuter l'application de démonstration :
java -cp ..\library\lexan.jar;. LexanDemo
Vous devriez voir les résultats suivants :
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
Le message Неожиданный символ во входном тексте: 20se produit à la suite d'une exception levée LexanExceptionen raison du fait que la classe BinTokensne déclare pas de constante Tokenavec une valeur 2sous forme d'expression régulière. Notez que le gestionnaire d'exceptions affiche la position du caractère inapproprié obtenue à partir de l'analyse lexicale du texte. Le message de jetons manquants est le résultat d'une exception levée LexExceptioncar NoTokensaucune constante n'est déclarée dans la classe Token.

Dans les coulisses

Lexanutilise la classe Lexan comme moteur. Jetez un œil à l'implémentation de cette classe dans le listing 4 et notez la contribution des expressions régulières pour rendre le moteur réutilisable. Listing 4. Création d'une architecture d'analyseur lexical basée sur des expressions régulières
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);
      }
   }
}
Le code de la méthode lex()est basé sur le code fourni dans l'article de blog « Writing a Parser in Java : A Token Generator » sur le site Web Cogito Learning. Lisez cet article pour en savoir plus sur la façon dont Lexan utilise l'API Regex pour compiler le code. Expressions régulières en Java, parties 5 à 4

Conclusion

Les expressions régulières sont un outil utile qui peut être utile à tout développeur. L'API Regex du langage de programmation Java les rend faciles à utiliser dans les applications et les bibliothèques. Maintenant que vous avez déjà une compréhension de base des expressions régulières et de cette API, jetez un œil à la documentation du SDK java.util.regexpour en savoir plus sur les expressions régulières et les méthodes supplémentaires de l'API Regex.
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION