Thomas Zilliox
Intégrateur CSS Freelance à Lyon

Sélectionner chaque mot d'une phrase avec une Regex

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.

Le besoin

Il y a plusieurs façon de compter les mots d'une phrase. J'ai personnellement besoin de suivre les règles suivantes :

  • une apostrophe doit être considérée comme un séparateur de mot
  • les mots composés comptent comme plusieurs mots

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' ]

Première idée

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.

  • Pas d'accent : « problème » se retrouve coupé en « probl » et « me » ;
  • Pas de « ç » : « ça » se retrouve limité à « a » ;
  • Pas de « œ » : « nœud » se retrouve coupé en « n » et « ud » ;

La solution

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

Position des diacritiques de la langue française dans la table unicode

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…

Pas encore la solution idéale

Au milieu des accents latins, les caractères mathématiques « × » et « ÷ »

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 !

Bienvenue dans le futur

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(', '));

En résumé

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 !

/^Happy (coding|vacations)$/, Thomas.

That's my face!

Thomas ZILLIOX

L'homme qui murmurait à l'oreille des chevrons.

Je développe, j'intègre, je forme ou je conseille sur les CSS. Besoin d'améliorer la maintenabilité ou les performances de vos projets ?