Écrire des expressions rationnelles performantes

Voici quelques-uns des aspects importants que vous devez prendre en compte lors de la création d'expressions rationnelles performantes.

Pour les structures d'expression régulière qui peuvent être utilisées avec des analyseurs, des étiquettes et des filtres de données, voir Documentation sur Java Platform Standard, éd. 8.

Dans le cas des consultations et de l'expression rationnelle d'interrogation, consultez la syntaxe RE2J sous Mise en oeuvre Java de RE2.

Classes de caractères

Les classes de caractères spécifient les caractères que vous essayez ou n'essayez pas de mettre en correspondance. Veillez à remplacer . dans votre .*s par un caractère plus spécifique. Le .* va invariablement atteindre la fin de l'entrée puis revenir en arrière, c'est-à-dire à l'état précédemment enregistré pour poursuivre la recherche d'une correspondance. Lorsque vous utilisez une classe de caractères spécifique, vous contrôlez le nombre de caractères que le * va faire consommer au moteur d'expressions rationnelles, ce qui vous permet d'arrêter les retours en arrière incontrôlables.

Prenons l'exemple d'expression rationnelle suivant :

(\d{4})(\d{2})(\d{2}),(\S+),([\S\s]*),([\S\s]*),([\S\s]*),([\S\s]*),([\S\s]*),([\S\s]*),([\S\s]*),([\S\s]*),([\S\s]*),([\S\s]*),([0-9.]+),([0-9.]+),([0-9.]+),([0-9.]+),([0-9.]+),([0-9.]+)

Pour l'entrée suivante :

20150220,201502,16798186260,tvN,Entertainment/Music,Female,67,2,Individual,ollehtv Economic,Commercial Housing,5587,0,2,0,1,1

Comme résultat de l'expression rationnelle spécifiée, la mise en correspondance entraînerait un retour en arrière. Cette situation est détectée par Oracle Logging Analytics et l'opération de mise en correspondance est abandonnée.

En remplaçant l'expression rationnelle par l'exemple ci-dessous, vous pouvez vous assurer que la mise en correspondance est plus rapide. Notez que [\S\s]* est remplacée par [^,], ce qui évite le retour arrière inutile.

(\d{4})(\d{2})(\d{2}),(\S+),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([0-9.]+),([0-9.]+),([0-9.]+),([0-9.]+),([0-9.]+),([0-9.]+)

Quantificateurs paresseux

Dans de nombreuses expressions rationnelles, les quantificateurs gourmands (.*s) peuvent être remplacés par des quantificateurs paresseux (.*?s), ce qui améliore la performance de l'expression rationnelle sans modifier le résultat.

Prenons l'entrée :

Trace file /u01/app/oracle/diag/rdbms/navisdb/NAVISDB/trace/NAVISDB_arc0_3941.trc
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
ORACLE_HOME = /u01/app/oracle/product/11.2.0/db_1
System name: Linux
Node name: NAVISDB
Release: 2.6.18-308.el5
Version: #1 SMP Fri Jan 27 17:17:51 EST 2012
Machine: x86_64
Instance name: NAVISDB
Redo thread mounted by this instance: 1
Oracle process number: 21
Unix process pid: 3941, image: oracle@NAVISDB (ARC0)

Examinons l'expression rationnelle gourmande suivante pour l'entrée indiquée :

Trace\sfile\s(\S*).*ORACLE_HOME\s*[:=]\s*(\S*).*System\sname:\s*(\S*).*Node\sname:\s*(\S*).*Release:\s*(\S*).*Machine:\s*(\S*).*Instance\sname:\s*(\S*).*Redo\sthread\smounted\sby\sthis\sinstance:\s(\d*).*Oracle\sprocess\snumber:\s*(\d*).*Unix\sprocess\spid:\s(\d*).*image:\s+([^\n\r]*)

Le moteur d'expressions rationnelles atteint la fin de l'entrée chaque fois qu'il détecte .*.. La première fois que la valeur .* apparaît, elle consomme toutes les entrées, puis revient en arrière jusqu'à ORACLE_HOME. Il s'agit d'une méthode de mise en correspondance inefficace. L'expression rationnelle paresseuse alternative est présentée ci-dessous :

Trace\sfileRelease:\s*(\S*).*?Machine:\s*(\S*).*?Instance\sname:\s*(\S*).*?Redo\sthread\smounted\sby\sthis\sinstance:\s(\d*).*?Oracle\sprocess\snumber:\s*(\d*).*?Unix\sprocess\spid:\s(\d*).*?image:\s+([^\n\r]*)

L'expression rationnelle paresseuse ci-dessus consomme du début de la chaîne jusqu'à ORACLE_HOME où elle pourra poursuivre la mise en correspondance du reste de la chaîne.

Note : Si le champ ORACLE_HOME apparaît vers le début de l'entrée, le quantificateur paresseux doit être utilisé. Si le champ ORACLE_HOME apparaît vers la fin, il peut être judicieux d'utiliser le quantificateur gourmand.

Ancres

Les ancres indiquent au moteur d'expressions rationnelles que vous voulez placer le curseur à un endroit particulier dans l'entrée. Les ancres les plus courantes sont ^ et $, indiquant le début et la fin de l'entrée.

Examinons les expressions rationnelles suivantes pour rechercher une adresse IPv4 :

\d{1,3}\.d{1,3}.\d{1,3}.\d{1,3}
^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}

Notez que la deuxième expression rationnelle commence par ^ et qu'elle est donc spécifique de l'adresse IP qui apparaît au début de l'entrée.

Nous recherchons dans l'entrée l'expression rationnelle qui ressemble à l'exemple suivant :

107.21.20.1 - - [07/Dec/2012:18:55:53 -0500] "GET
      /extension/bsupport/design/cl/images/btn_letschat.png HTTP/1.1" 200
    2144

Une entrée sans correspondance ressemble à l'exemple suivant :

[07/Dec/2012:23:57:13 +0000] 1354924633 GET "/favicon.ico" "" HTTP/1.1 200 82726 "-"
      "ELB-HealthChecker/1.0"

La deuxième expression rationnelle (qui commence par ^) s'exécute plus rapidement sur l'entrée non correspondante car elle l'ignore immédiatement.

L'importance de l'ordre

L'ordre étant important, vous devez placer les options les plus courantes au début pour qu'elles puissent trouver une correspondance plus rapidement. Si les options plus rares sont devant, le moteur d'expressions rationnelles va perdre du temps à les vérifier avant de traiter les options plus courantes qui ont plus de chance de succès. Par ailleurs, essayez d'extraire les modèles courants. Par exemple, au lieu de (abcd|abef), utilisez ab(cd|ef).

Observons les expressions rationnelles suivantes :

{TIMEDATE}\s{0,1}:\s*(?:|\[)(\w+)(?:\:|\])(?:\[|)(\d+)(?:\:|\])(.{1,1000}).*
{TIMEDATE}\s{0,1}:\s*(?:\[|)(\w+)(?:\:|\])(?:\[|)(\d+)(?:\:|\])(.{1,1000}).*

Dans l'entrée suivante :

2014-06-16 12:13:46.743: [UiServer][1166092608] {0:7:2} Done for
ctx=0x2aaab45d8330

La deuxième expression rationnelle correspond plus rapidement car l'alternance recherche d'abord le caractère [, suivi d'une valeur nulle. Comme l'entrée contient [, la mise en correspondance s'exécute plus rapidement.

Exemples d'expression Parse

Vous pouvez consulter les exemples d'expressions Parse suivants pour créer une expression Parse appropriée pour extraire des valeurs de votre fichier journal.

Un fichier journal comprend des entrées générées par la concaténation de plusieurs valeurs de champ. Il est possible que vous n'ayez pas besoin de consulter toutes les valeurs de champ pour analyser un fichier journal d'un format particulier. À l'aide d'un analyseur, vous pouvez extraire les valeurs des champs que vous voulez consulter.

Un analyseur extrait les champs d'un fichier journal en fonction de l'expression Parse que vous avez définie. Une expression Parse est écrite sous la forme d'une expression rationnelle qui définit un modèle de recherche. Dans une expression Parse, vous placez les modèles de recherche entre parenthèses (), pour chaque champ correspondant que vous voulez extraire d'une entrée de journal. Toute valeur correspondant à un modèle de recherche qui se trouve en dehors des parenthèses n'est pas extraite.

Exemple 1

Pour analyser les exemples d'entrées de journal suivants :

Jun 20 15:19:29 hostabc rpc.gssd[2239]: ERROR: can't open clnt5aa9: No such file or directory
Jul 29 11:26:28 hostabc kernel: FS-Cache: Loaded
Jul 29 11:26:28 hostxyz kernel: FS-Cache: Netfs 'nfs' registered for caching

Votre expression Parse doit se présenter comme suit :

(\S+)\s+(\d+)\s(\d+):(\d+):(\d+)\s(\S+)\s(?:([^:\[]+)(?:\[(\d+)\])?:\s+)?(.+)

Dans l'exemple précédent, certaines des valeurs saisies par l'expression Parse sont les suivantes :

  • (\S+) : Plusieurs caractères autres que blancs pour le mois

  • (\d+) : Plusieurs caractères autres que blancs pour le jour

  • (?:([^:\[]+) : (Facultatif) Tous les caractères sauf ^, :, \, []; il s'agit du nom du service

  • (.+) : (Facultatif) Contenu du message principal

Exemple 2

Pour analyser les exemples d'entrées de journal suivants :

####<Apr 27, 2014 4:01:42 AM PDT> <Info> <EJB> <host> <AdminServer> <[ACTIVE] ExecuteThread: '0' for queue: 'weblogic.kernel.Default (self-tuning)'> <OracleSystemUser> <BEA1-13E2AD6CAC583057A4BD> <b3c34d62475d5b0b:6e1e6d7b:143df86ae85:-8000-000000000000cac6> <1398596502577> <BEA-010227> <EJB Exception occurred during invocation from home or business: weblogic.ejb.container.internal.StatelessEJBHomeImpl@2f9ea244 threw exception: javax.ejb.EJBException: what do i do: seems an odd quirk of the EJB spec. The exception is:java.lang.StackOverflowError>
####<Jul 30, 2014 8:43:48 AM PDT> <Info> <RJVM> <example.com> <> <Thread-9> <> <> <> <1406735028770> <BEA-000570> <Network Configuration for Channel "AdminServer" Listen Address example.com:7002 (SSL) Public Address N/A Http Enabled true Tunneling Enabled false Outbound Enabled false Admin Traffic Enabled true ResolveDNSName Enabled false> 

Votre expression Parse doit se présenter comme suit :

####<(\p{Upper}\p{Lower}{2})\s+([\d]{1,2}),\s+([\d]{4})\s+([\d]{1,2}):([\d]{2}):([\d]{2})\s+(\p{Upper}{2})(?:\s+(\w+))?>\s+<(.*?)>\s+<(.*?)>\s+<(.*?)>\s+<(.*?)>\s+<(.*?)>\s+<(.*?)>\s+<(.*?)>\s+<(.*?)>\s+<\d{10}\d{3}>\s+<(.*?)>\s+<(.*?)(?:\n(.*))?>\s*

Dans l'exemple précédent, certaines des valeurs saisies par l'expression Parse sont les suivantes :

  • (\p{Upper}\p{Lower}{2}) : Nom abrégé de 3 lettres pour le mois, avec la première lettre en majuscules suivie de deux lettres en minuscules

  • ([\d]{1,2}) : jour à 1 ou 2 chiffres

  • ([\d]{4}) : année à 4 chiffres

  • ([\d]{1,2}) : heure à 1 ou 2 chiffres

  • ([\d]{2}) : minute à 2 chiffres

  • ([\d]{2}) : seconde à 2 chiffres

  • (\p{Upper}{2}) : AM/PM à 2 lettres en majuscules

  • (?:\s+(\w+)) : (Facultatif, certaines entrées ne peuvent pas retourner de valeur pour ce paramètre) Plusieurs caractères alphanumériques pour le fuseau horaire

  • (.*?) : (Facultatif, certaines entrées ne peuvent pas retourner de valeur pour ce paramètre) Un ou plusieurs caractères pour le niveau de gravité; dans ce cas <INFO>

  • (.*) : Tout détail supplémentaire avec le message

Modèles de recherche

Certains des modèles couramment utilisés sont expliqués dans le tableau suivant :

Modèle Description Exemple
. Tout caractère à l'exception du saut de ligne d.f correspond à def, daf, dbf, etc.
* Zéro fois ou plus D*E*F* correspond à DDEEFF, DEF, DDFF, EEFF, etc.
? Une fois ou aucune; facultatif colou?r correspond à la fois à colour et color
+ Une ou plusieurs Stage \w-\w+ correspond à l'étape A-b1_1, à l'étape B-a2, etc.
{2} Exactement deux fois [\d]{2} correspond à 01, 11, 21, etc.
{1,2} Deux à quatre fois [\d]{1,2} correspond à 1, 12, etc.
{3,} Trois fois ou plus [\w]{3,} correspond à ten, hello, h2134, etc.
[ … ] Un des caractères entre crochets [AEIOU] correspond à une voyelle en majuscules
[x-y] Un des caractères dans l'intervalle de x à y [A-Z]+ correspond à ACT, ACTION, BAT, etc.
[^x] Un caractère qui n'est pas x [^/d]{2} correspond à AA, BB, AC, etc.
[^x-y] Un des caractères ne figure pas dans l'intervalle de x à y [^a-z]{2} correspond à A1, BB, B2, etc.
[\d\D] Un caractère qui est un chiffre ou un caractère non numérique [\d\D]+ correspond à tout caractère, y compris les nouvelles lignes, dont le point standard ne correspond pas
\s Un blanc (\S+)\s+(\d+) correspond à AA 123, a_ 221, etc.
\S Un caractère qui n'est pas un blanc (\S+) correspond à abcd, ABC, A1B2C3, etc.
\n Une nouvelle ligne (\d)\n(\w) correspond à :

1

A

\w Un caractère alphanumérique [\w-\w\w\w] correspond à a-123, 1-aaa, etc.
\p{Lower} Lettres minuscules \p{Lower}{2} correspond à aa, ab, ac, bb, etc.
\p{Upper} Lettres majuscules \p{Upper} correspond à A, B, C, etc.
\ suivi de ?, [], *, . Caractère d'échappement; pour utiliser les caractères après \ comme littéraux \? retourne ?