12 juillet 2023

Alternance (OU) |

Alternance est le terme d’expression régulière qui représente un “OU”.

Dans une expression régulière l’alternance est représentée par une barre verticale |.

Par exemple, nous souhaitons trouver les langages de programmation suivants: HTML, PHP, Java ou JavaScript.

La regexp correspondante : html|php|java(script)?.

Exemple d’utilisation:

let regexp = /html|php|css|java(script)?/gi;

let str = "First HTML appeared, then CSS, then JavaScript";

alert( str.match(regexp) ); // 'HTML', 'CSS', 'JavaScript'

Nous avons déjà vu une chose similaire, les crochets. Ils permettent de choisir entre plusieurs caractères, par exemple gr[ae]y correspond à gray ou grey.

Les crochets n’autorisent que les caractères ou les classes de caractère. L’alternance permet n’importe quelle expression. Une regexp A|B|C signifie A, B ou C.

Par exemple:

  • gr(a|e)y signifie la même chose que gr[ae]y.
  • gra|ey signifie gra ou ey.

Pour appliquer l’alternance à une partie du modèle nous pouvons l’encadrer entre parenthèses:

  • I love HTML|CSS correspond à I love HTML ou CSS.
  • I love (HTML|CSS) correspond à I love HTML ou I love CSS.

Exemple: regexp d’un temps

Dans les articles précédents il y avait une tâche qui consistait à construire une regexp pour trouver un temps de la forme hh:mm, par exemple 12:00. Mais un simple modèle \d\d:\d\d est trop vague. Il accepte 25:99 comme temps (puisque “99 minutes” correspond au modèle, mais ce temps est invalide).

Comment pouvons-nous créer un meilleur modèle ?

Nous pouvons utiliser une correspondance plus appropriée. Premièrement, les heures :

  • Si le premier chiffre est 0 ou 1, alors le prochain chiffre peut être: [01]\d.
  • Sinon, si le premier chiffre est 2, alors le prochain doit être [0-3].
  • (aucun autre premier chiffre est autorisé)

Nous pouvons écrire les deux variantes dans une regexp en utilisant l’alternance: [01]\d|2[0-3].

Ensuite, les minutes doivent être entre 00 et 59. Dans le langage des expression régulières cela peut être écrit [0-5]\d: le premier chiffre 0-5, puis n’importe quel chiffre.

Si nous rejoignons les heures et les minutes ensemble, nous obtenons le modèle: [01]\d|2[0-3]:[0-5]\d.

Nous y sommes presque, mais il y a un problème. L’alternance | se trouve désormais entre [01]\d et 2[0-3]:[0-5]\d.

Cela signifie que les minutes sont incluses dans la seconde variante d’alternance, voici un affichage plus clair:

[01]\d  |  2[0-3]:[0-5]\d

Ce modèle recherche [01]\d ou 2[0-3]:[0-5]\d.

Mais c’est incorrect, l’alternance ne devrait être utilisé que pour la partie “heures” de l’expression régulière, pour permettre [01]\d OU 2[0-3]. Corrigeons cela en mettant les “heures” entre parenthèses : ([01]\d|2[0-3]):[0-5]\d.

La solution finale :

let regexp = /([01]\d|2[0-3]):[0-5]\d/g;

alert("00:00 10:10 23:59 25:99 1:2".match(regexp)); // 00:00,10:10,23:59

Exercices

Il y a beaucoup de langages de programmation, par exemple Java, JavaScript, PHP, C, C++.

Créez une regexp qui les trouve dans une chaine de caractère Java JavaScript PHP C++ C :

let regexp = /your regexp/g;

alert("Java JavaScript PHP C++ C".match(regexp)); // Java JavaScript PHP C++ C

La première idée peut être de lister les langages avec des | entre deux.

Mais cela ne fonctionne pas correctement :

let regexp = /Java|JavaScript|PHP|C|C\+\+/g;

let str = "Java, JavaScript, PHP, C, C++";

alert( str.match(regexp) ); // Java,Java,PHP,C,C

Le moteur d’expression régulière regarde les alternances une par une. C’est-à-dire : il regarde d’abord si nous avons Java, sinon il recherche JavaScript et ainsi de suite.

Ainsi, JavaScript ne peut jamais être trouvé, puisque Java est vérifié en premier.

Pareil pour C et C++.

Il y a deux solutions à ce problème :

  1. Changer l’ordre pour vérifier le mot le plus long en premier : JavaScript|Java|C\+\+|C|PHP.
  2. Fusionner les mots commençant de la même manière : Java(Script)?|C(\+\+)?|PHP.

En action :

let regexp = /Java(Script)?|C(\+\+)?|PHP/g;

let str = "Java, JavaScript, PHP, C, C++";

alert( str.match(regexp) ); // Java,JavaScript,PHP,C,C++

Un “bb-tag” ressemble à [tag]...[/tag], où tag peut être : b, url ou quote.

Par exemple :

[b]text[/b]
[url]http://google.com[/url]

Les BB-tags peuvent être imbriqués. Mais un tag ne peut pas être imbriqué dans lui même, par exemple :

Normal:
[url] [b]http://google.com[/b] [/url]
[quote] [b]text[/b] [/quote]

Ne peut pas arriver:
[b][b]text[/b][/b]

Les tags peuvent contenir des sauts de ligne, c’est normal :

[quote]
  [b]text[/b]
[/quote]

Créez une regexp pour trouver tous les BB-tags avec leur contenu.

Par exemple :

let regexp = /your regexp/flags;

let str = "..[url]http://google.com[/url]..";
alert( str.match(regexp) ); // [url]http://google.com[/url]

Si les tags sont imbriqués, alors nous voulons le tag extérieur (si nous voulons nous pouvons continuer la recherche dans le contenu) :

let regexp = /your regexp/flags;

let str = "..[url][b]http://google.com[/b][/url]..";
alert( str.match(regexp) ); // [url][b]http://google.com[/b][/url]

Un tag d’ouverture est \[(b|url|quote)\].

Ensuite pour trouver tout jusqu’au tag de fermeture, utilisons le modèle .*? avec le flag s pour trouver n’importe quel caractère en plus des sauts de ligne, puis ajoutons une référence au tag de fermeture.

Le modèle complet : \[(b|url|quote)\].*?\[/\1].

En action :

let regexp = /\[(b|url|quote)].*?\[\/\1]/gs;

let str = `
  [b]hello![/b]
  [quote]
    [url]http://google.com[/url]
  [/quote]
`;

alert( str.match(regexp) ); // [b]hello![/b],[quote][url]http://google.com[/url][/quote]

Veuillez noter qu’en plus d’échapper [, nous avons dû échapper une barre oblique pour la balise de fermeture [\/\1], car normalement la barre oblique ferme le modèle.

Créer une regexp pour trouver les chaines de caractère entre guillemets doubles "...".

La chaine de caractère devrait supporter l’échappement, comme les chaines de caractère JavaScript. Par exemple, des guillemets peuvent être insérés comme ceci \" une nouvelle ligne comme \n, et un antislash comme \\.

let str = "Just like \"here\".";

Veuillez noter qu’une guillemet échapée \" ne termine pas une chaine de caractère.

Nous devrions donc chercher une guillemet puis la suivante en ignorant celles échapées.

C’est la partie essentielle de la tâche, à part cela, cela devrait être simple.

Exemple de chaine de caractère valides :

.. "test me" ..
.. "Say \"Hello\"!" ... (guillemets échapées à l'intérieur)
.. "\\" ..  (double backslash à l'intérieur)
.. "\\ \"" ..  (double backslash et guillemets échapées à l'intérieur)

En JavaScript nous devons doubler les slash pour les placer dans la chaine de caractère, comme ceci :

let str = ' .. "test me" .. "Say \\"Hello\\"!" .. "\\\\ \\"" .. ';

// the in-memory string
alert(str); //  .. "test me" .. "Say \"Hello\"!" .. "\\ \"" ..

La solution : /"(\\.|[^"\\])*"/g.

Etape par etape :

  • D’abord nous recherchons une guillemet ouvrante "
  • Ensuite si nous avons un antislash \\ (puisque c’est un caractère spécial nous devons le doubler, mais dans les faits c’est un unique antislash), alors n’importe quel caractère peut se trouver à sa suite (un point).
  • Sinon nous prenons n’importe quel caractère à part une guillemet (cela signifierait la fin de la chaine de caractère) et un antislash (pour empêcher les antislash solitaires, un antislash est seulement utilisé avec un autre symbole après lui): [^"\\]
  • …Et on continue jusqu’à atteindre la guillemet fermante.

En action :

let regexp = /"(\\.|[^"\\])*"/g;
let str = ' .. "test me" .. "Say \\"Hello\\"!" .. "\\\\ \\"" .. ';

alert( str.match(regexp) ); // "test me","Say \"Hello\"!","\\ \""

Écrivez une regexp pour trouver la balise <style...>. Il devrait trouver la balise en entier: il pourrait ne pas avoir d’attributs <style> ou en avoir plusieurs <style type="..." id="...">.

…Mais la regexp ne devrait pas trouver <styler>!

Par exemple:

let regexp = /your regexp/g;

alert( '<style> <styler> <style test="...">'.match(regexp) ); // <style>, <style test="...">

Le début du modèle est évident: <style.

…Mais nous ne pouvons pas juste écrire <style.*?> puisque <styler> y correspondrait.

Nous avons besoin soit d’un espace après <style et après optionellement quelque chose d’autre, ou bien la fin >.

Dans le langage des regexp : <style(>|\s.*?>).

En action :

let regexp = /<style(>|\s.*?>)/g;

alert( '<style> <styler> <style test="...">'.match(regexp) ); // <style>, <style test="...">
Carte du tutoriel