• Contenu
  • Bas de page
blog-ouidou_logoblog-ouidou_logoblog-ouidou_logoblog-ouidou_logo
  • Actualités Ouidou
  • Articles Techniques
  • Livres blancs
  • Interviews Clients
  • Interviews collaborateurs
Découvrir Ouidou
✕
No results See all results
Les tâches dans VS Code

Les tâches dans VS Code

21 novembre 2019
Mettez JavaScript au régime avec Svelte

Mettez JavaScript au régime avec Svelte

6 février 2020
Ressources > Comparaison de chaînes équivalentes en Ruby

Comparaison de chaînes équivalentes en Ruby

Article écrit par François Vantomme

Il apparait, à certains moments de la vie d’un développeur, la nécessité de comparer des chaînes de caractères. Cela peut être pour retrouver un mot dans un texte, ou encore pour interpréter une réponse à un questionnaire. Situation somme toute assez classique, mais qui peut néanmoins s’avérer délicate !

Si je vous demande le nom de ce vieux bonhomme barbu et vêtu de rouge qui parcourt le monde à une vitesse supraluminique et se glisse dans les cheminées — et ce malgré un embonpoint non dissimulé — pour en ressortir sans une trace de suie, j’aimerais alors être en mesure d’accepter tout aussi bien « Père Noël », « Pere Noel » que « PERE NOEL ».

En effet, lorsque les chaînes à comparer ne sont pas entièrement maîtrisées — comme votre réponse à la question ci-dessus, par exemple — on peut se retrouver à vouloir considérer celles-ci comme étant équivalentes, sans pour autant qu’elles soient strictement identiques.

Je vous propose, à l’aide de quelques exemples en Ruby, d’explorer quelques situations qu’il peut arriver de rencontrer.

Caractères ASCII (ou à raquettes)

Le cas le plus simple est celui où la casse des caractères diffère.

"Ruby" != "RUBY" 

Pourtant, en développeur magnanime, on aimerait bien considérer ces chaînes comme équivalentes. Ruby nous offre pour cela de nombreux outils de manipulation de chaînes, qui nous permettront de comparer ce que l’on appellera, dans cet article, la forme canonique de nos chaînes.

MATH. Qui a les qualités d’un canon, d’une norme dominante. Forme, équation canonique. — CNRTL

L’idée étant de comparer des chaînes comparables en donnant à chacune d’elle une forme adaptée à nos besoins. Pour reprendre l’exemple ci-dessus, on pourrait décider de convertir l’ensemble des caractères en majuscule. Ce qui donnerait ceci :

"Ruby".upcase == "RUBY".upcase 

À noter qu’on aurait tout aussi bien pu s’accorder sur une conversion en minuscules.

Unicode mon amour

Tout se complique lorsque l’on quitte les réconfortantes limites d’un clavier QWERTY pour explorer le monde merveilleux de l’Unicode !

En effet, il ne faut pas s’égarer fort longtemps, notamment pour un francophone, avant de rencontrer une lettre accompagnée d’un diacritique. Voyons comment se comporte String#upcase dans ces cas-là :

"Père Noël".upcase # => "PÈRE NOËL" 

Eh oui, les majuscules aussi s’accentuent ! Et Ruby gère ça très bien depuis sa version 2.4… c’est-à-dire depuis à peine 3 ans. Enfin, très bien…

En toute franchise, Unicode c’est un chouïa compliqué, et on arrive très rapidement à des situations tout à fait cocasses. Prenons quelques exemples et essayons de comprendre ce qui se passe.

s1 = ["N", "o", "ë", "l"].join # => "Noël" s2 = ["N", "o", "e", "̈", "l"].join # => "Noël" s1 == s2 # => false 

On s’aperçoit ici que selon la manière dont on a composé notre chaîne de caractères — à partir de caractères précomposés ou composables — Ruby se comportera différemment. Et ce, même en version 2.7.0, sortie ce 25 décembre 2019 !

Encodage et normalisation

C’est là qu’entre en jeu la normalisation Unicode.

Lorsque les implémentations conservent des chaînes de caractères sous une forme normalisée, elles peuvent être assurées que des chaînes de caractères équivalentes ont une représentation binaire unique.

— Unicode® Standard Annex #15, libre traduction

Ruby dispose justement d’une méthode String#unicode_normalize pour ce faire.

s1 = ["N", "o", "ë", "l"].join.unicode_normalize s2 = ["N", "o", "e", "̈", "l"].join.unicode_normalize s1 == s2 # => true 

Excellent ! Et si on creuse un peu, on découvre qu’il existe en réalité quatre méthodes de normalisation :

  • NFD : décomposition canonique
  • NFC : décomposition canonique, suivie de la composition canonique
  • NFKD : décomposition de compatibilité
  • NFKC : décomposition de compatibilité, suivie de la composition canonique

Pour les besoins de l’exercice, prenons pour exemple la lettre minuscule latine long s point en chef, notamment utilisée dans les manuscrits gaéliques d’Irlande.

Pour briller en société : Les mathématiques emploient un dernier avatar du s long comme symbole de l’intégrale : ∫. Le co-inventeur du concept de somme intégrale, Leibniz, a utilisé le premier mot de l’expression en latin, summa, somme, écrit ſumma et en a conservé l’initiale.   — Wikipédia

"ẛ".unicode_normalize(:nfd).chars # => ["ſ", "̇"]  "ẛ".unicode_normalize(:nfc).chars # => ["ẛ"]  "ẛ".unicode_normalize(:nfkd).chars # => ["s", "̇"]  "ẛ".unicode_normalize(:nfkc).chars # => ["ṡ"] 

On remarque ici que les formes en D (NFD et NFKD) vont avoir pour effet de décomposer le caractère initial en un caractère de base, suivi d’un ou plusieurs diacritiques.

"ᾇ".unicode_normalize(:nfd).chars # => ["α", "̔", "͂", "ͅ"] 

Tandis que les formes en K (NFKD et NFKC) vont mettre l’accent, si je puis dire, sur la compatibilité. Cela se remarque tout particulièrement sur l’alphabet arabe, dont les symboles voient leur graphie adaptée en fonction de leur positionnement dans le mot !

irb(main):076:0> "ﻍ ﻏﻐﻎ".unicode_normalize(:nfc).chars # => ["ﻍ", " ", "ﻏ", "ﻐ", "ﻎ"]  irb(main):074:0> "ﻍ ﻏﻐﻎ".unicode_normalize(:nfkc).chars # => ["غ", " ", "غ", "غ", "غ"] 

Rassemblons nos billes

Rappelons-le, notre problématique de base était de pouvoir comparer des chaînes de caractères suffisamment semblables pour pouvoir les considérer comme équivalentes.

Avec ce que nous venons de voir, et une petite expression rationnelle — oui, j’en suis certain, ça vous avait manqué — nous pouvons facilement arriver à nos fins !

La regex en question n’est pas bien complexe. Nous allons nous contenter d’exclure, une fois notre chaîne décomposée, toute accentuation qui y serait présente. C’est-à-dire, toute lettre modificative avec chasse (allant de u02B0 à u02FF) ou diacritique (allant de u0300 à u036F).

Voici le résultat :

def to_canonical(str)   return "" if str.empty?    characters = str.unicode_normalize(:nfkd).chars.reject do |c|       c =~ /[u02B0-u02FFu0300-u036F]/     end   end    characters.join.upcase end 

En décomposant les différentes étapes, on obtient ceci :

pn = "Père Noël" pn.unicode_normalize(:nfkd) # => "Père Noël" _.chars # => ["P", "e", "̀", "r", "e", " ", "N", "o", "e", "̈", "l"] _.reject { |c| c =~ /[u02B0-u02FFu0300-u036F]/ } # => ["P", "e", "r", "e", " ", "N", "o", "e", "l"] _.join # => "Pere Noel" _.upcase # => "PERE NOEL" 

Ainsi, les chaînes suivantes sont équivalentes :

p1 = to_canonical("Pere Noel") # => "PERE NOEL" p2 = to_canonical("Père Noël") # => "PERE NOEL" p1 == p2 # => true 

Ce qu’il faut retenir

La manipulation de chaînes de caractères, malgré les nombreux outils fournis par Ruby en standard, peut rapidement s’avérer complexe dès lors qu’on explore un tant soit peu les possibilités que nous offre Unicode, tant dans son support des nombreuses graphies que l’homme a inventé au cours des siècles, que dans leurs multiples représentations.

Chaque situation trouvera une réponse qui lui est adaptée. Pour peu qu’on ait à l’esprit les écueils que l’on risque de rencontrer, il existera un moyen de les contourner. Mais ce moyen diffèrera selon que l’on aborde la question sous l’angle de l’accessibilité, de la localisation, ou de la sécurité par exemple.

Si vous voulez aller plus loin, vous pouvez consulter la documentation et l’implémentation de la gem twitter_cldr qui se repose depuis 2014 sur la très efficiente gem eprun, à parcourir également.

Et pour poursuivre votre lecture (en anglais) je vous conseille la très excellente suite d’articles de DaftCode dont le dernier volet s’intitule Fixing Unicode for Ruby developers.

À lire aussi

Diagnostiquer, comprendre et optimiser les performances d’une application React
4 mai 2026

Diagnostiquer, comprendre et optimiser les performances d’une application React

Lire la suite de l'article

Ouicommit Paris – IA : de l’expérimentation à l’industrialisation
15 avril 2026

Ouicommit Paris – IA : de l’expérimentation à l’industrialisation

Lire la suite de l'article

Ouidou et Scaleway : un partenariat stratégique pour accélérer le cloud souverain en Europe
8 avril 2026

Ouidou et Scaleway : un partenariat stratégique pour accélérer le cloud souverain en Europe

Lire la suite de l'article

Replay webinaire Ouidou x Sonar
2 juillet 2026

Replay webinaire Ouidou x Sonar

Lire la suite de l'article

Articles associés

Ouicommit Paris – IA : de l’expérimentation à l’industrialisation
15 avril 2026

Ouicommit Paris – IA : de l’expérimentation à l’industrialisation


Lire la suite
Ouidou et Scaleway : un partenariat stratégique pour accélérer le cloud souverain en Europe
8 avril 2026

Ouidou et Scaleway : un partenariat stratégique pour accélérer le cloud souverain en Europe


Lire la suite
Comment préparer votre Jira Cloud dès maintenant ? 
14 novembre 2025

Comment préparer votre Jira Cloud dès maintenant ? 


Lire la suite
Replay webinaire IA
5 novembre 2025

Replay webinaire IA


Lire la suite
  • Linkedin Ouidou
  • Youtube Ouidou

Blog Ouidou, le rendez-vous des équipes tech qui veulent comprendre avant de décider. Veille, retours d'expérience et décryptages pour rester à la pointe.

© 2026 Ouidou | Tous droits réservés | Plan du site | Mentions légales | Déclaration d'accessibilité
    Découvrir Ouidou
    No results See all results