Récemment, j'ai voulu sélectionner tous les mots d'une phrase à l'aide d'une expression régulière. J'étais persuadé que ce serait facile, comme toujours, et finalement c'était plus subtil que prévu, comme toujours.
Il y a plusieurs façon de compter les mots d'une phrase. J'ai personnellement besoin de suivre les règles suivantes :
Un cas de test complet
const sentence = `
Du toboggan sur des arcs-en-ciel ?
Le nœud du problème ça reste l'atterrissage !
`;
/* Something like an unicorn regex well summon up */
const words = unicorn(sentence);
console.log(words);
// Should ouputs 15 words
// [ 'Du', 'toboggan', 'sur', 'des', 'arcs', 'en', 'ciel', 'Le', 'nœud', 'du', 'problème', 'ça', 'reste', 'l', 'atterrissage' ]
Autant le dire clairement, je ne peux pas juste découper en suivant les espaces. Après un peu de recherche, je trouve ce que
je pense être la solution parfaite : simple et élégante. Il existe une classe abrégée qui réprésente
« les caractères qui composent les mots » : \w
😎️
Exemple de code interactif utilisant \w
const sentence = `
Du toboggan sur des arcs-en-ciel ?
Le nœud du problème ça reste l'atterrissage !
`;
const words = sentence.match(/\w+/g);
console.log(words.join(', '));
Le problème est que cette classe abrégée est en fait un raccourci pour la classe de caractères [a-zA-Z0-9_]
.
Du coup, ça ne prend aucun accent ou caractère spécial de la langue française.
La plupart des accents sont regroupés dans deux blocs unicode assez bas : le bloc supplément latin-1 à partir de U+00C0 et le bloc Latin étendu – A à partir de U+0100
On peut donc sélectionner « les caractères qui composent les mots latins » en ciblant également ces
deux blocs unicode. Dans la langue des expressions régulières, cela correspond à la classe de caractères [a-zA-ZÀ-ž]
.
Exemple de code interactif utilisant [a-zA-ZÀ-ž]
const sentence = `
Du toboggan sur des arcs-en-ciel ?
Le nœud du problème ça reste l'atterrissage !
`;
const words = sentence.match(/[a-zA-ZÀ-ž]+/g);
console.log(words.join(', '));
C'est plutôt court et élégant. Cela devrait correspondre à votre besoin dans 99,99% des cas ! 👍️
Mais…
Pourquoi ? 😭️
Pourquoi avoir placé les caractères réprésentant la multiplication et la division en plein milieu du bloc unicode « supplément latin-1 » ?
Il faudrait donc utiliser la classe de caractères [A-Za-zÀ-ÖØ-öø-ž]
pour éviter
de considérer des symboles mathématiques comme des lettres.
C'est tout de suite moins élégant !
C'est un cas d'usage clairement identifié. Il existe bien une classe de caractères pour toutes les lettres de n'importe
quelle langue : \p{letter}
, voir \p{L}
pour l'écriture raccourcie. Merci à dhoko de me l'avoir pointé du doigt !
Les classes de caractères de propriétés unicode s'écrivent sous la forme \p{nom}
et permettent
de sélectionner les caractères unicode en fonction de leur catégorie. Côté JavaScript, c'est assez
récent. Ça a été ajouté dans la spécification d'ES2018. Cela veut dire que seuls les navigateurs récents
les supportent ! Il vous faudra activer manuellement le support en ajoutant le flag //u
.
La liste des propriétés définies par TC39 est disponible dans la spécification d'ECMAScript.
Exemple de code interactif utilisant \p{L}
const sentence = `
Du toboggan sur des arcs-en-ciel ?
Le nœud du problème ça reste l'atterrissage !
`;
const words = sentence.match(/\p{L}+/gu);
console.log(words.join(', '));
Voici les classes de caractères que je vais essayer de retenir pour mes prochaines regex :
/\w+/ // Les lettres minuscules, majuscules, les nombres et le underscore
/[a-zA-ZÀ-ž]+/ // Les lettres minuscules, majuscules et accentuées
/[A-Za-zÀ-ÖØ-öø-ž]+/ // Les lettres minuscules, majuscules et accentuées, mais pas × et ÷
/\p{L}+/u // Toutes les lettes, mais attention au support !