Les pièges de l'encodage lors de l'ouverture de fichiers textes.

Lorsqu'on utilise les méthodes de la classe TFile (unité System.IOutils) pour manipuler des fichiers textes il est possible de spécifier un encodage lors de la lecture comme lors de l'écriture. Il en va de même des méthodes LoadFromFile() et SaveToFile() sur les listes de chaînes et différents composants en contenant. Des fois ça ne fait pas exactement ce qu'on attend, mais c'est normal...

Lors de l'ouverture des fichiers Delphi tente de détecter le bon format pour choisir le bon encodage et le bon mappage. Avec un BOM (UTF-8 ou UTF-16) en entête ça lui simplifie la tâche.

S'il n'y a pas de BOM en début de fichier l'ouverture se fait en ANSI (sous Windows) ou en UTF-8 (pour les autres plateformes). Ca peut donner des résultats aberrants lorsque le fichier contenait des caractères débordant du classique 1 octet = 1 caractère de la table ASCII étendue ou des pages de code Windows. La documentation de Delphi explique ce mécanisme.

Vous aurez reconnu au passage le début du livre Le Tour du monde en 80 jours de Jules Verne.

Pour éviter ces soucis il faut donc spécifier le bon encodage lorsqu'on en attend un. Mais attention : utiliser le mauvais peut avoir des effets de bord.

Par exemple, lire un fichier ANSI avec un encodage UTF-8 est susceptible de retourner une exception EEncodingError. En revanche, lire un fichier UTF-8 en utilisant un encodage ANSI ne posera pas de problème, si ce n'est de rendre le texte pas très lisible.

Alors quoi faire lorsqu'on attend essentiellement des fichiers UTF-8 ou qu'on ne sait pas quel encodage auront les fichiers ouverts par notre logiciel ? Essayer les deux, tout simplement.

Voici quelques lignes de code Pascal qui devraient vous aider à passer à travers les mailles de ce problème de base dans l'informatique moderne...

  try
    ch := tfile.ReadAllLines('..\..\ansi.txt', tencoding.utf8);
  except
    on EEncodingError do
      ch := tfile.ReadAllLines('..\..\ansi.txt', tencoding.ansi);
  end;

Dans le cas de mes tests j'avais un fichier ansi.txt et un fichier utf8.txt, les deux contenant le même texte mais bien entendu dans des encodages différents.

L'ouverture du fichier ansi.txt déclenche une exception lorsqu'on essaie en UTF-8, du coup on l'ouvre juste après en ANSI.
Les mêmes lignes de codes avec un fichier encodé en UTF-8 passent correctement dès le départ.

Deux problèmes à ça :

  • Le fichier n'a pas intérêt à être trop volumineux, parce qu'on l'ouvre deux fois dans le pire des cas et ça peut ralentir le programme.
  • Il n'y a pas que UTF-8 et ANSI comme encodages possibles !

On peut difficillement tous les tester à chaque ouverture de fichier texte donc mieux vaut s'assurer que tous les documents exportés depuis vos logiciels sont avec le même encodage que celui que vous attendez en entrée et refuser au maximum les données qui ne sont pas conformes à vos attentes. Reste le problème de compatibilité ascendante avec l'existant si vos logiciels ont une base ancienne d'utilisateurs.

Pour rappel, en version 10.4.1 Sydney de Delphi nous avons ces choix possibles sur la classe TEncoding :

  • Default => ANSI sous Windows, UTF-8 ailleurs, donc à éviter autant que possible.
  • ANSI
  • ASCII
  • BigEndianUnicode
  • Unicode
  • UTF7
  • UTF8

Ceci dit, n'oubliez pas que si vous manipulez des fichiers dans des threads il est fortement recommandé d'intercepter et gérer les exceptions qu'ils sont susceptibles de générer. Vous risquez dans le cas contraire de les planter ce qui peut impacter vos interfaces utilisateurs selon ce que vous y faites.


A lire aussi

Evitons des violations d'accès aléatoires dans les tâches (09/10/2020)
Les pièges de l'encodage lors de l'ouverture de fichiers textes. (21/09/2020)
Insérer un enregistrement dans un ensemble de données depuis une grille avec LiveBindings (30/05/2020)
Eviter les fuites de mémoire lors de la manipulation d'objets JSON (11/05/2020)
Pourquoi vouloir ajouter ()?: au Pascal alors qu'on a ifthen() ? (11/05/2020)
Plutôt INI ou JSON pour stocker vos paramètres ? (11/05/2020)
Quel composant utiliser pour dialoguer sur le port série de l'ordinateur ? (31/05/2019)
Utilisation de processus sous Delphi : fonctionnement de base. (30/07/2018)
Les threads et le blocage des écrans (30/07/2018)
Evitez les plantages causés par une mauvaise utilisation de la librairie System.JSON (18/04/2018)
Calculer et vérifier un checksum pour dialoguer avec l'extérieur (19/07/2017)
Télécharger simplement un fichier via Internet en tâche de fond (10/07/2017)
Calculer un MD5 sous Delphi (04/07/2017)
Passer un traitement lourd en tâche de fond sans bloquer l'écran (04/06/2017)
Ajouter des chaînes de caractères vides dans un objet JSON (19/05/2017)
Configurer le firewall de McAfee AntiVirus Plus pour utiliser l'App Tethering (28/06/2016)

Membre du programme MVP.
Membre du programme MVP