Détection automatique de l’encodage d’un fichier

Ceux qui ont déjà dû traiter des fichiers textes et notamment des fichiers CSV, savent déjà de quoi je vais parler, la difficulté pour traiter l’encodage lors de la lecture (qui provoque notamment les fameux caractères bizarres en lieu et place des accents).

La solution consiste à détecter l’encodage utilisé pour écrire le fichier.

Il existe plusieurs méthodes pour détecter celui-ci, je vais vous en présenter deux, qui ont une approche totalement différente mais complémentaire.

Utiliser le BOM

Cette méthode, la plus connue et la moins couteuse, consiste à utiliser le BOM (Byte Order Mark). Ces quelques octets permettent de déterminer l’encodage d’un fichier. Cependant, cette méthode ne se suffit pas à elle-même car dans certains cas le BOM n’est pas présent (par exemple UTF8-sans bom) et ne peux donc être lu.

public static Encoding DetectEncodingWithBom(Stream stream)
{
    // Lecture des 4 bytes du BOM
    var bom = new byte[4];
    var position = stream.Position;

    // On se positionne au début du stream    
    stream.Seek(0, SeekOrigin.Begin);

    stream.Read(bom, 0, 4);

    // On repositionne le stream à la position originale
    stream.Seek(position, SeekOrigin.Begin);
    
    // Analyse de ces 4 bytes
    if (bom[0] == 0x2b && bom[1] == 0x2f && bom[2] == 0x76)
    {
        // UTF7
        return Encoding.UTF7;
    }

    if (bom[0] == 0xef && bom[1] == 0xbb && bom[2] == 0xbf)
    {
        // UTF8
        return Encoding.UTF8;
    }

    if (bom[0] == 0xff && bom[1] == 0xfe)
    {
        // UTF-16LE
        return Encoding.Unicode;
    }

    if (bom[0] == 0xfe && bom[1] == 0xff)
    {
        // UTF-16BE
        return Encoding.BigEndianUnicode;
    }

    if (bom[0] == 0 && bom[1] == 0 && bom[2] == 0xfe && bom[3] == 0xff)
    {
        return Encoding.UTF32;
    }

    // Aucun encodage connu n'a pu être déterminé par la lecture du BOM
    return null;
}

 

UDE

UDE (https://github.com/errepi/ude) est un portage C# du projet Mozilla Universal Chartset Detector qui se base sur le contenu du fichier pour déterminer l’encodage. Pour ceux que ça intéresse, voici un article en présentant comment fonctionne cette approche (http://www-archive.mozilla.org/projects/intl/UniversalCharsetDetection.html).

Pour mettre en place UDE dans votre application C#, il faut ajouter le package NuGet UDE.CSharp et utiliser ensuite la classe ChartsetDetector. Vous allez nourrir celle-ci avec les bytes du fichier d’origine jusqu’à ce que la détection de l’encodage soit terminée. Mais attention, cette méthode peut être particulièrement couteuse puisqu’elle peut potentiellement parcourir tout le contenu du fichier afin de déterminer l’encodage utilisé. Son utilisation peut donc avoir un impact non négligeable d’un point de vue performances. Voici un exemple complet :

public static Encoding DeductEncoding(Stream stream)
{
    Encoding detectedEncoding = null;
    var position = stream.Position;

    if (stream.Length <= 0)
    {
        return detectedEncoding;
    }

    // On se positionne au début du stream    
    stream.Seek(0, SeekOrigin.Begin);

    var detectBuff = new byte[4096];

    // Utilisation de ChartsetDetector pour en déduire l'encodage
    var chartsetDetector = new CharsetDetector();
    while (stream.Read(detectBuff, 0, detectBuff.Length) > 0 && !chartsetDetector.IsDone())
    {
        chartsetDetector.Feed(detectBuff, 0, detectBuff.Length);
    }

    chartsetDetector.DataEnd();

    // Obtention du Chartset déduit
    var detectedCharset = chartsetDetector.Charset;
    if (detectedCharset != null)
    {
        // Selon le résultat, obtenir l'Encoding associé
        switch (detectedCharset)
        {
            case "UTF-8":
                detectedEncoding = Encoding.UTF8;
                break;
            case "windows-1252":
                detectedEncoding = Encoding.GetEncoding(1252);
                break;
            // Ajouter les autres encodages que la librairie détecte (cf la page Github)
            // et que vous souhaitez supporter dans votre application
        }
    }

    chartsetDetector.Reset();

    // On repositionne le stream à la position originale
    stream.Seek(position, SeekOrigin.Begin);

    return detectedEncoding;
}

Un prérequis pour les deux méthodes est que le stream d’entrée puisse être “seekable” afin de pouvoir le positionner au début pour effectuer les opérations de détection.

Les deux méthodes combinées permettent d’avoir de bon résultat dans la détection de l’encodage d’un fichier. C’est pourquoi, je vous conseille dans un premier temps d’utiliser la méthode avec le BOM et si celle-ci ne renvoie pas d’encodage, d’utiliser la méthode avec UDE.

Bon code

Photo de profil

Ces billets pourraient aussi vous intéresser

Vous nous direz ?!

Commentaires

comments powered by Disqus