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 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
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.
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èresc
, 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 PLUS correspondant 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. |
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 packageca.javajeff.lexan
inclus dans le code téléchargeable de cet article :
Lexan
: analyseur lexical ;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.
LexanException
: exception levée dans le constructeur de classeLexan;
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.Class
correspondant au type constant class static Token
. À l'aide de l'API Reflection, le constructeur lit toutes les constantes Token
dans un tableau de valeurs Token[]
. S'il Token
n'y a pas de constantes, une exception est levée LexanException
. La classe Lexan
fournit é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 typeTokLex
. Si un caractère rencontré ne correspond à aucun des modèles de tableauToken[]
, une exception est levéeLexException
.
LexanException
n'a pas de méthodes ; elle utilise une méthode héritée pour renvoyer un message d'exception getMessage()
. En revanche, la classe LexException
fournit 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.
Token
remplace 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 TokLex
fournit 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,Lexan
j'ai écrit une application LexanDemo
. Il se compose de classes LexanDemo
, BinTokens
et MathTokens
. NoTokens
Le code source de l'application LexanDemo
est 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 Class
et la chaîne à analyser. La méthode lex()
crée d'abord un objet de la classe Lexan
en passant l'objet Class
au constructeur de classe Lexan
. Et puis il appelle la méthode lex()
de classe Lexan
sur cette chaîne. Si l'analyse lexicale réussit, la méthode de classe TokLex
est 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()
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_]*");
}
Le listing 3 montre que la classe MathTokens
dé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. La position de la constante Token
dans la liste des marqueurs est importante. Les constantes situées plus haut dans la liste Token
ont priorité sur celles situées en dessous. Par exemple, lorsqu'il rencontre sin
, Lexan choisit le jeton FUNC
au lieu de ID
. Si le marqueur ID
avait 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 archivelexan.zip
contenant tous les fichiers de la distribution Lexan. Décompressez cette archive et accédez à un sous-répertoire demos
du 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 Неожиданный символ во входном тексте: 20
se produit à la suite d'une exception levée LexanException
en raison du fait que la classe BinTokens
ne déclare pas de constante Token
avec une valeur 2
sous 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 LexException
car NoTokens
aucune constante n'est déclarée dans la classe Token
.
Dans les coulisses
Lexan
utilise 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.
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 SDKjava.util.regex
pour en savoir plus sur les expressions régulières et les méthodes supplémentaires de l'API Regex.
GO TO FULL VERSION