Changer nos habitudes pour traiter les caractères dans un monde où Unicode, UTF-8, UTF-16 et UTF-32 sont devenus la norme.

Dans certains programmes je retraite des chaînes de caractères pour les épurer de leurs caractères accentués. Ca me sert par exemple lorsque je génère des identifiants de fichiers, des noms de constantes à utiliser dans les programmes ou des URL de pages web.

Depuis mes premiers programmes en Pascal puis Delphi j'utilisais l'opérateur in qui permet de vérifier si un élément appartient à un ensemble. Ca fonctionnait très bien pour les caractères ANSI/ASCII où un caractère était stocké sur 1 octet, mais ce n'est plus le cas.

var c : char;
begin
  if c in ['a', 'b'] then
    DoSomething;
end;

Si vous avez compilé vos programmes utilisant cet opérateur sur des char vous devriez avoir un message indiquant que le compilateur a réduit le WideChar en byte char car in ne fonctionne que sur des octets. En effet un char est désormais un WideChar Unicode par défaut. Ca ne pose pas de soucis pour la gamme de caractères que l'on utilisait historiquement, mais dès qu'on veut utiliser des symboles hors de la place 0-255, ça ne fonctionne plus.

Depuis la version 2009 qui a introduit l'Unicode dans Delphi on pouvait utiliser la fonction CharInSet de l'unité System.SysUtils. Mais cette fonction ne gère pas d'Unicode du côté de la liste des caractères dans lesquels on en recherche un. En revanche elle ne pose plus de soucis avec le message d'avertissement du compilateur.

var c : char;
begin
  if CharInSet(c, ['a', 'b']) then
    DoSomething;
end;

Pour traiter correctement les caractères Unicode il existe une unité dédiée : System.Character. Elle a beaucoup évolué au fil des versions et propose maintenant le helper TCharHelper qui permet de faire tout un tas de tests sur un caractère dans son encodage actuel. On doit donc adapter les deux versions de code historique avec cette nouvelle forme.

var c : char;
begin
  if c.isInArray(['a', 'b']) then
    DoSomething;
end;

L'avantage, c'est qu'on peut comparer des caractères dans toute l'étendue de la gamme Unicode. L'IDE le détecte et propose d'enregistrer notre unité en UTF-8 au lieu de l'ANSI par défaut si on en saisit dans le code source.

Et c'est comme ça qu'on peut écrire une fonction de conversion de texte dans un format exploitable en identifiant Pascal / Delphi à partir de n'importe quel texte (moyennant d'adapter la liste des caractères pris en charge au fur et à mesure) :

function ToDelphiConst(Texte: string): string;
var
  c: char;
  i: Integer;
  PremierCaractere: boolean;
begin
  Result := '';
  Texte := Texte.Trim;
  PremierCaractere := true;
  for i := 0 to Length(Texte) - 1 do
  begin
    c := Texte.Chars[i];
    if (c in ['0' .. '9']) then
    begin
      if PremierCaractere then
        Result := Result + '_' + c
      else
        Result := Result + c;
    end
    else if (c in ['a' .. 'z']) or (c in ['A' .. 'Z']) then
      Result := Result + c
    else if c.IsInArray(['@']) then
      Result := Result + '_'
    else if c.IsInArray(['#']) then
      Result := Result + '_'
    else if c.IsInArray(['£']) then
      Result := Result + '_'
    else if c.IsInArray(['€']) then
      Result := Result + 'EUR'
    else if c.IsInArray(['$']) then
      Result := Result + 'USD'
    else if c.IsInArray(['_', '-', ' ']) then
      Result := Result + '_'
    else if c.IsInArray(['à', 'â', 'ä']) then
      Result := Result + 'a'
    else if c.IsInArray(['é', 'è', 'ë', 'ê']) then
      Result := Result + 'e'
    else if c.IsInArray(['ï', 'î']) then
      Result := Result + 'i'
    else if c.IsInArray(['ô', 'ö']) then
      Result := Result + 'o'
    else if c.IsInArray(['ü', 'û', 'ù']) then
      Result := Result + 'u'
    else if c.IsInArray(['Š']) then
      Result := Result + 'S'
    else if c.IsInArray(['ž']) then
      Result := Result + 'z'
    else if c.IsInArray(['ç', 'č']) then
      Result := Result + 'c';
    PremierCaractere := false;
  end;
  while Result.IndexOf('__') > -1 do
    Result := ReplaceText(Result, '__', '_');
end;

Me suis penché sur la question lors de la création d'un site référençant les livres publiés sur Delphi afin de pouvoir obtenir une URL à partir des noms d'auteurs brésiliens. Je m'en sers dans de nombreux autres projets, n'hésitez pas à vous en inspirer si vous avez ce type de besoin.

Notez que j'ai laissé les intervales "ANSI" pour la détection de lettres et de chiffres classiques. La version avec tableau oblige à saisir toutes les lettres/chiffres pour que ça passe puisque l'écriture avec les ".." n'est pas valide dans un type array().

Pendant que j'y suis, si Unicode vous titille, n'oubliez pas de lire la documentation de Delphicet article explique comment c'est intégré et quels sont les impacts dans nos programmes.


A lire aussi

Changer nos habitudes pour traiter les caractères dans un monde où Unicode, UTF-8, UTF-16 et UTF-32 sont devenus la norme. (19/10/2020)
Les enregistrements managés ou managed RECORD en Pascal (24/05/2020)
Pour ne pas perdre la mémoire, mangez du poisson ! (03/03/2019)
Modification corrective de l'assistant split() sur les chaînes de caractères sous Delphi 10.3 Rio (23/01/2019)
Les variables inline, quoi qu'est-ce ? (09/11/2018)
On a enfin eu la peau de l'ARC sur les objets !!! (29/10/2018)
Jouons un peu avec les types énumérés et leurs valeurs (05/12/2017)
Simplifier le passage de listes en paramètres et leur création (24/07/2017)

Membre du programme MVP.
Membre du programme MVP