Cliquez sur un bouton pour naviguer

4. Usage

La section précédente couvrait la syntaxe des expressions régulières. Elle utilisait l'interface la plus simple avec le comparateur : envoyer le message #matchesRegex: à la chaîne à analyser avec l'expression régulière en argument. Cette section présente des usages plus complexes du comparateur.

CORRESPONDANCE D'UN PREFIXE ET CORRESPONDANCE NE TENANT PAS COMPTE DE LA CASSE

Une chaîne (classe String) comprend aussi ces messages :

#prefixMatchesRegex: regexString
 

#matchesRegexIgnoringCase: regexString

  #prefixMatchesRegexIgnoringCase: regexString

Les 2 derniers messages sont des versions insensibles à la casse des comparateurs.

#prefixMatchesRegex: est identique à #matchesRegex, excepté que le récepteur n'a pas à correspondre entièrement à l'expression régulière passée en argument. Correspondre au début du récepteur suffit. Par exemple :

'abcde' matchesRegex: '(a|b)+' -- false
 

'abcde' prefixMatchesRegex: '(a|b)+'

-- true

INTERFACE D'ÉNUMÉRATION

Il peut être intéressant pour une application de connaître toutes les correspondances d'une expression régulière avec une chaîne. On accède aux correspondances en utilisant un protocole d'énumération utilisé par les classes de type Collection :

#regex: regexString matchesDo: unBloc  

Évalue un bloc à un argument unBloc pour chaque correspondance de l'expression régulière avec la chaîne réceptrice.

#regex: regexString matchesCollect: unBloc  

Évalue un bloc à un argument unBloc pour chaque correspondance de l'expression régulière avec la chaîne réceptrice. Collecte les résultats de l'évaluation et les renvoie comme une SequenceableCollection.

#allRegexMatches: regexString  

Renvoie une collection de toutes les correspondances (sous-chaînes de la chaîne réceptrice) de l'es l'expression régulière. Équivalentà aString regex: regexString matchesCollect: [each | each].

REMPLACEMENT ET TRADUCTION

Il est possible de remplacer toutes les correspondances d'une expression régulière avec une certaines chaîne en utilisant le message :

#copyWithRegex: regexString matchesReplacedWith: aString  

Par exemple :

'ab cd ab' copyWithregex: '(a|b)+' matchesReplacesWith: 'foo'

Un mode de substitution plus général est la traduction des correspondances :

#copyWithRegex: regexString matchesTranslatedUsing: unBloc

Ce message évalue un bloc en lui passant chaque correspondance de l'expression régulière avec le récepteur et en retournant une copie du récepteur modifié. Chaque correspondance en fonction des résultats du bloc. Par exemple :

'ab cd ab' copyWithregex: '(a|b)+' matchesTranslatedUsing: [:each| each asUppercase]

Tous les messages des protocoles d'énumération et de remplacement sont sensibles à la casse. Les versions non sensibles ne font pas parties du protocole CharacterArray. Elles sont disponibles par une interface de bas niveau.

INTERFACE DE BAS NIVEAU

Le fonctionnement de #matchesRegex est le suivant :

  1. Une instance de RxParser est créée et la chaîne de l'expression régulière lui est passée créant un arbre syntaxique de l'expression.
  2. L'arbre syntaxique est passé comme paramètre d'initialisation à une instance de RxMatcher. Cette instance met en place quelques structures de données qui serviront d'élément de reconnaissance pour l'expression régulière décrite par l'arbre.
  3. La chaîne originale est passée à l'instance de RxMatcher qui va rechercher des correspondances.

LE COMPARATEUR

Si vous comparez de façon répétitive un certain nombre de chaînes à la même expression régulière en utilisant les messages de la classe String que nous venons de voir, l'expression régulière est analysée et un comparateur est créé pour chaque nouvelle comparaison. Vous pouvez éviter ce gaspillage en construisant un comparateur pour l'expression régulière et en le réutilisant autant que nécessaire. Vous pouvez par exemple créer un comparateur au moment de l'initialisation d'une classe ou d'une instance, et le stocker dans une variable pour un usage ultérieur.

Vous pouvez créer un comparateur en utilisant une de ces méthodes :

Une manière plus pratique consiste à utiliser un des 2 messages de création de comparateur compris par les chaînes.

Voici 4 exemples de création de comparateurs :

hexRecognizer := RxMatcher forString: '16r[0-9A-Fa-f]+'
hexRecognizer := RxMatcher forString: '16r[0-9A-Fa-f]+' ignoreCase: false
hexRecognizer := '16r[0-9A-Fa-f]+' asRegex
hexRecognizer := '16r[0-9A-F]+' asRegexIngnoringCase

CORRESPONDANCE

Le comparateur comprend ces messages (tous retournent true pour indiquer une comparaison ou une recherche fructueuse, et false autrement) :

matches: aString

  Vrai si la chaîne cible correspond entièrement

matchesPrefix: aString

  Vrai si le début de la chaîne cible correspond (pas nécessairement toute la chaîne)

search: aString

  Cherche la première occurence d'une sous-chaîne. (Notez que les 2 premières méthodes essaient de trouver une correspondanceà partir du tout début de la chaîne). Si l'on utilise l'exemple ci-dessus avec un comparateur pour 'a+', cette méthode retourne true si on lui passe l'argument 'baaa', alors que les 2 autres méthodes retournent false.

matchesStream: aStream
matchesStreamPrefix: aStream
searchStream: aStream

  Respectivement analogues aux 3 méthodes précédentes, elle prennent un Stream et non une chaîne en argument. Ce Stream doit être positionnable et lisible.

Toutes ces méthodes retournent un booléen. Le comparateur stocke la dernière valeur retournée et peut la restituer :

lastResult

  Retourne un booléen -- le résultat de la dernière comparaison. Si aucune comparaison n'a été effectuée, le résultat est indéterminé.

CORRESPONDANCES DE SOUS-EXPRESSIONS

Après une comparaison fructueuse, vous pouvez demander quelle partie de la chaîne originale a été reconnue par quelle partie de l'expression régulière.

Une sous-expression est une partie de l'expression régulière mise entre parenthèses, ou l'expression en entier. Quand une expression régulière est compilée, ses sous-expressions se voient assigner un indice commençant à 1, de gauche à droite. Par exemple, '((ab)+(c|d))?ef' comprend les sous-expressions suivantes avec leurs indices :

1 ((ab)+(c|d))?ef l'indice 1 représente l'expression complète
  2  

(ab)+(c|d)

 
  3   ab  
  4   c|d  

Après une comparaison fructueuse, le comparateur peut indiquer quelle partie de la chaîne originale correspond à quelle sous-expression. Il comprend ces messages :

subexpressionCount

  Renvoie le nombre total de sous-expressions : la plus haute valeur qui peut être utilisée comme index avec ce comparateur. Cette valeur est disponible immédiatement après l'initialisation et est constante.

subexpressionCount: unIndex

  unIndex doit être valide (valeur inférieure ou égale à subexpressionCount) et ce message ne peut être envoyé qu'après une comparaison réussie. Cette méthode retourne une sous-chaîne correspondant à la sous-expression.

subBeginning: unIndex
subEnd: unIndex

  Retourne les positions dans la chaîne originale ou le Stream, où la correspondance de la sous-expression pointée par l'index a respectivement commencé, et terminé.

L'utilitaire qui suit permet d'extraire des éléments d'une de structure complexe. Par exemple le code suivant utilise l'expression au format 'MMM JJ, 19YY' que nous avons déjà vu à la section Syntaxe, pour convertir une date en un tableau de 3 éléments :

|matcher|
matcher := Rxmatcher forString: '(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[ ]+(:isDigit::isDigit:?)[ ]*,[ ]*19(:isDigit::isDigit:)'.
(matcher matches: 'Aug 6, 1996')
     ifTrue:
        [Array
            with: (matcher subexpression: 4)
            with: (matcher subexpression: 2)
            with: (matcher subexpression: 3)]
     ifTrue: ['no match']

(devrait retourner '#('96' 'Aug' '6')').

ÉNUMÉRATION ET REMPLACEMENT

Les protocoles d'énumération et de remplacement utilisés avec les tableaux de caractères sont en fait implémentés par le comparateur. Les messages suivants sont définis :

#matchesIn: uneChaine
#matchesIn: uneChaine do: unBloc
#matchesIn: uneChaine collect: unBloc
#copy: uneChaine replacingMatchesWith: chaineDeRemplacement
#copy: uneChaine translatingMatchesUsing: unBloc
 
#matchesOnStream: unStream
#matchesOnStream: unStream do: unBloc
#matchesOnStream: unStream collect: unBloc
#copy: streamSource to: streamCible replacingMatchesWith: chaineDeRemplacement
#copy: streamSource to: streamCible translatingMatchesWith: unBloc

GESTION DES ERREURS

Si une erreur de syntaxe est détectée pendant l'analyse de l'expression, erreur RxSyntaxError est levée.

Si une erreur est détectée pendant la construction d'un comparateur, une erreur RxCompilationError est levée.

Si une erreur est détectée pendant une comparaison (par exemple si une sélecteur incorrect a été spécifié en utilisant la syntaxe :<sélecteur>:, ou à cause d'une erreur interne au comparateur, une erreur RxMatcherError est levée.

Ces trois erreurs ont RxError pour parent. Puisque ces 3 signaux peuvent être levés lors d'un appel à #matchesRegex, RxError est commode pour les capturer tous les 3. Par exemple :

'abc' matchesRegex: '))garbage['
    on: RxError
    do: [:ex | ex return: false]

Dernière modification le 10-03-2002