web-dev-qa-db-fra.com

Comment puis-je détecter le codage/page de code d'un fichier texte

Dans notre application, nous recevons des fichiers texte (.txt, .csv, etc.) provenant de sources diverses. Lors de la lecture, ces fichiers contiennent parfois des ordures, car ils ont été créés dans une page de codes différente/inconnue.

Existe-t-il un moyen de détecter (automatiquement) la page de codes d'un fichier texte? 

La detectEncodingFromByteOrderMarks, sur le constructeur StreamReader, fonctionne pour UTF8 et les autres fichiers marqués Unicode, mais je cherche un moyen de détecter les pages de codes, comme ibm850, windows1252


Merci pour vos réponses, voici ce que j'ai fait.

Les fichiers que nous recevons proviennent d'utilisateurs finaux. Ils n'ont aucune idée des pages de codes. Les destinataires sont également des utilisateurs finaux. À présent, voici ce qu’ils connaissent des pages de codes: Les pages de codes existent et sont gênantes.

Solution:  

  • Ouvrez le fichier reçu dans le Bloc-notes, regardez un morceau de texte tronqué. Si quelqu'un s'appelle François ou quelque chose comme ça, avec votre intelligence humaine, vous pouvez deviner.
  • J'ai créé une petite application que l'utilisateur peut utiliser pour ouvrir le fichier et entrer un texte que l'utilisateur sait qu'il apparaîtra dans le fichier lorsque la page de codes correcte est utilisée. 
  • Parcourez toutes les pages de codes et affichez celles qui offrent une solution avec le texte fourni par l'utilisateur. 
  • Si plusieurs pages de code apparaissent, demandez à l'utilisateur de spécifier plus de texte.
283
GvS

Vous ne pouvez pas détecter la page de codes, vous devez le savoir. Vous pouvez analyser les octets et les deviner, mais cela peut donner des résultats bizarres (parfois amusants). Je ne le trouve pas maintenant, mais je suis sûr que le Bloc-notes peut être amené à afficher du texte anglais en chinois. 

Quoi qu'il en soit, voici ce que vous devez lire: Le minimum absolu que chaque développeur de logiciel a absolument, doit savoir absolument sur Unicode et les jeux de caractères (sans excuses!) .

Plus précisément, Joël dit:

Le fait le plus important concernant les codages

Si vous oubliez complètement tout ce que je viens d’expliquer, rappelez-vous un fait extrêmement important. Il n’a pas de sens d’avoir une chaîne sans savoir quel encodage elle utilise. Vous ne pouvez plus vous mettre la tête dans le sable et prétendre que le texte "ordinaire" est au format ASCII . Il n'y a aucune chose telle que le texte simple.

Si vous avez une chaîne, en mémoire, dans un fichier ou dans un courrier électronique, vous devez savoir dans quel encodage il se trouve ou vous ne pouvez pas l'interpréter ni l'afficher correctement aux utilisateurs.

255
JV.

Si vous souhaitez détecter des codages non-UTF (c.-à-d. Pas de nomenclature), il vous faut essentiellement une analyse heuristique et statistique du texte. Vous voudrez peut-être jeter un coup d'œil au papier Mozilla sur la détection de jeu de caractères universel ( même lien, avec un meilleur formatage via Wayback Machine ).

30
Tomer Gabel

Avez-vous essayé le port C # du détecteur de charset universel Mozilla

Exemple tiré de http://code.google.com/p/ude/

public static void Main(String[] args)
{
    string filename = args[0];
    using (FileStream fs = File.OpenRead(filename)) {
        Ude.CharsetDetector cdet = new Ude.CharsetDetector();
        cdet.Feed(fs);
        cdet.DataEnd();
        if (cdet.Charset != null) {
            Console.WriteLine("Charset: {0}, confidence: {1}", 
                 cdet.Charset, cdet.Confidence);
        } else {
            Console.WriteLine("Detection failed.");
        }
    }
}    
21
ITmeze

Vous ne pouvez pas détecter la page de codes

Ceci est clairement faux. Chaque navigateur Web possède une sorte de détecteur de jeux de caractères universel pour traiter les pages qui n’ont aucune indication d’un codage. Firefox en a un. Vous pouvez télécharger le code et voir comment il le fait. Voir la documentation ici . Fondamentalement, c'est une heuristique, mais qui fonctionne vraiment bien.

Avec une quantité de texte raisonnable, il est même possible de détecter la langue.

Voici un autre Je viens de trouver en utilisant Google:

15
shoosh

Je sais qu'il est très tard pour cette question et que cette solution ne plaira pas à certains (en raison de son biais anglais et de son absence de tests statistiques/empiriques), mais cela a très bien fonctionné pour moi, en particulier pour le traitement des données CSV téléchargées:

http://www.architectshack.com/TextFileEncodingDetector.ashx

Avantages:

  • Détection de nomenclature intégrée
  • Encodage par défaut/de secours personnalisable
  • très fiable (selon mon expérience) pour les fichiers basés en Europe occidentale contenant des données exotiques (par exemple des noms français) avec un mélange de fichiers de style UTF-8 et de style Latin-1 - essentiellement le gros des environnements américain et ouest-européen.

Remarque: je suis celui qui a écrit cette classe, donc prenez-le évidemment avec un grain de sel! :)

8
Tao

A la recherche d'une solution différente, j'ai trouvé que 

https://code.google.com/p/ude/

cette solution est un peu lourde.

J'avais besoin d'une détection d'encodage de base, basée sur 4 premiers octets et probablement d'une détection de jeu de caractères xml - j'ai donc pris un exemple de code source sur Internet et ajouté une version légèrement modifiée de

http://lists.w3.org/Archives/Public/www-validator/2002Aug/0084.html

écrit pour Java.

    public static Encoding DetectEncoding(byte[] fileContent)
    {
        if (fileContent == null)
            throw new ArgumentNullException();

        if (fileContent.Length < 2)
            return Encoding.ASCII;      // Default fallback

        if (fileContent[0] == 0xff
            && fileContent[1] == 0xfe
            && (fileContent.Length < 4
                || fileContent[2] != 0
                || fileContent[3] != 0
                )
            )
            return Encoding.Unicode;

        if (fileContent[0] == 0xfe
            && fileContent[1] == 0xff
            )
            return Encoding.BigEndianUnicode;

        if (fileContent.Length < 3)
            return null;

        if (fileContent[0] == 0xef && fileContent[1] == 0xbb && fileContent[2] == 0xbf)
            return Encoding.UTF8;

        if (fileContent[0] == 0x2b && fileContent[1] == 0x2f && fileContent[2] == 0x76)
            return Encoding.UTF7;

        if (fileContent.Length < 4)
            return null;

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

        if (fileContent[0] == 0 && fileContent[1] == 0 && fileContent[2] == 0xfe && fileContent[3] == 0xff)
            return Encoding.GetEncoding(12001);

        String probe;
        int len = fileContent.Length;

        if( fileContent.Length >= 128 ) len = 128;
        probe = Encoding.ASCII.GetString(fileContent, 0, len);

        MatchCollection mc = Regex.Matches(probe, "^<\\?xml[^<>]*encoding[ \\t\\n\\r]?=[\\t\\n\\r]?['\"]([A-Za-z]([A-Za-z0-9._]|-)*)", RegexOptions.Singleline);
        // Add '[0].Groups[1].Value' to the end to test regex

        if( mc.Count == 1 && mc[0].Groups.Count >= 2 )
        {
            // Typically picks up 'UTF-8' string
            Encoding enc = null;

            try {
                enc = Encoding.GetEncoding( mc[0].Groups[1].Value );
            }catch (Exception ) { }

            if( enc != null )
                return enc;
        }

        return Encoding.ASCII;      // Default fallback
    }

Il suffit de lire probablement les 1024 premiers octets du fichier, mais je charge tout le fichier.

7
TarmoPikaro

Notepad ++ a cette fonctionnalité prête à l'emploi. Il prend également en charge le changement.

7
hegearon

Si quelqu'un recherche une solution à 93,9%. Cela fonctionne pour moi:

public static class StreamExtension
{
    /// <summary>
    /// Convert the content to a string.
    /// </summary>
    /// <param name="stream">The stream.</param>
    /// <returns></returns>
    public static string ReadAsString(this Stream stream)
    {
        var startPosition = stream.Position;
        try
        {
            // 1. Check for a BOM
            // 2. or try with UTF-8. The most (86.3%) used encoding. Visit: http://w3techs.com/technologies/overview/character_encoding/all/
            var streamReader = new StreamReader(stream, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true), detectEncodingFromByteOrderMarks: true);
            return streamReader.ReadToEnd();
        }
        catch (DecoderFallbackException ex)
        {
            stream.Position = startPosition;

            // 3. The second most (6.7%) used encoding is ISO-8859-1. So use Windows-1252 (0.9%, also know as ANSI), which is a superset of ISO-8859-1.
            var streamReader = new StreamReader(stream, Encoding.GetEncoding(1252));
            return streamReader.ReadToEnd();
        }
    }
}
5
Magu

J'ai fait quelque chose de similaire en Python. Fondamentalement, vous avez besoin d’un grand nombre d’échantillons de données provenant de divers codages, qui sont décomposés par une fenêtre glissante de deux octets et stockés dans un dictionnaire (hachage), saisis sur des paires d’octets fournissant des valeurs de listes de codages.

Étant donné ce dictionnaire (hash), vous prenez votre texte d'entrée et:

  • si elle commence par un caractère de nomenclature ('\ xfe\xff' pour UTF-16-BE, '\ xff\xfe' pour UTF-16-LE, '\ xef\xbb\xbf' pour UTF-8, etc.), I traitez-le comme suggéré
  • sinon, prenez un échantillon de texte suffisamment volumineux, prenez toutes les paires d'octets de l'échantillon et choisissez le codage le moins courant suggéré dans le dictionnaire.

Si vous avez également échantillonné des textes codés en UTF qui pas commencent par une nomenclature, la deuxième étape couvrira ceux qui ont glissé depuis la première étape.

Jusqu'ici, cela fonctionne pour moi (les données d'échantillon et les données d'entrée suivantes sont des sous-titres dans différentes langues) avec des taux d'erreur décroissants.

4
tzot

L'outil "uchardet" le fait bien en utilisant des modèles de distribution de fréquence de caractère pour chaque jeu de caractères. Les fichiers plus volumineux et les fichiers plus "typiques" ont plus de confiance (évidemment).

Sur Ubuntu, vous venez de apt-get install uchardet

Sur d’autres systèmes, obtenez la source, l’utilisation et la documentation ici: https://github.com/BYVoid/uchardet

3
Erik Aronesty

Le constructeur de la classe StreamReader prend un paramètre 'detect encoding'.

3
leppie

Si vous pouvez créer un lien vers une bibliothèque C, vous pouvez utiliser libenca. Voir http://cihar.com/software/enca/ . De la page de manuel:

Enca lit les fichiers texte donnés, ou l’entrée standard quand aucun fichier n’est donné, et utilise des connaissances sur leur langue (vous devez les soutenir) et un mélange d'analyse syntaxique, d'analyse statistique, de devinettes et de magie noire pour déterminer leurs encodages.

C'est la GPL v2.

1
Kundor

Vous avez le même problème, mais vous n’avez pas encore trouvé de bonne solution pour le détecter automatiquement ... Maintenant, je me sers de PsPad (www.pspad.com) pour cela;)

0
DeeCee

Merci @ Erik Aronesty d’avoir mentionné uchardet.

Entre-temps, le même outil existe pour linux: chardet.
Ou, sur cygwin, vous pouvez utiliser: chardetect.

Voir: Page de manuel de chardet:https://www.commandlinux.com/man-page/man1/chardetect.1.html

Cela détectera de manière heuristique (deviner) le codage de caractères pour chaque fichier donné et indiquera le nom et le niveau de confiance du codage de caractères détecté pour chaque fichier.

0
Schlacki

En fait, je cherchais un moyen générique de détecter le codage du fichier, mais pas celui de la programmation, mais je ne l’avais pas trouvé pour le moment ..___.

Donc, là où je travaillais pour la première fois: StreamReader file = File.OpenText (fullfilename);

Je devais le changer en: StreamReader file = new StreamReader (fullfilename, System.Text.Encoding.UTF7);

OpenText suppose que c'est UTF-8.

vous pouvez également créer le StreamReader comme cecinew StreamReader (fullfilename, true), le deuxième paramètre signifiant qu'il doit essayer de détecter le codage à partir du byteordermark du fichier, mais cela n'a pas fonctionné dans mon cas.

0
Intraday Tips

En tant que complément à la publication ITmeze, j'ai utilisé cette fonction pour convertir la sortie du port C # du détecteur de charset universel Mozilla.

    private Encoding GetEncodingFromString(string codePageName)
    {
        try
        {
            return Encoding.GetEncoding(codePageName);
        }
        catch
        {
            return Encoding.ASCII;
        }
    }

MSDN

0
PrivatePyle

Ouvrez le fichier dans AkelPad (ou copiez/collez un texte tronqué), sélectionnez Edition -> Sélection -> Recoder ... -> cochez la case "Détection automatique".

0
plavozont

Comme il s'agit essentiellement d'heuristiques, il peut être utile d'utiliser comme premier indice le codage de fichiers précédemment reçus provenant de la même source.

La plupart des gens (ou des applications) font les choses dans le même ordre à chaque fois, souvent sur le même ordinateur. Il est donc fort probable que, lorsque Bob crée un fichier .csv et l'envoie à Mary, il utilisera toujours Windows 1252 ou quel que soit le comportement par défaut de sa machine.

Dans la mesure du possible, un peu de formation client ne fait pas de mal non plus :-)

0
devstuff

10Y (!) Était écoulé depuis que cette question avait été posée, et je ne vois toujours aucune mention de la bonne solution de MS sans GPL: IMultiLanguage2 API.

La plupart des bibliothèques déjà mentionnées sont basées sur l'UDE de Mozilla - et il semble raisonnable que les navigateurs aient déjà abordé des problèmes similaires. Je ne sais pas quelle est la solution de chrome, mais depuis IE 5.0 MS ont publié la leur, elle se présente comme suit:

  1. Libre de problèmes de licence GPL et autres,
  2. Soutenu et maintenu probablement pour toujours,
  3. Donne une sortie riche - tous les candidats valides pour l’encodage/les pages de code avec les scores de confiance,
  4. Étonnamment facile à utiliser (il s’agit d’un appel à fonction unique).

Il s’agit d’un appel COM natif, mais voici un très beau travail de Carsten Zeumer, qui gère le désordre d’interopérabilité pour l’utilisation du .net. Il y en a d’autres, mais dans l’ensemble, cette bibliothèque n’obtient pas l’attention qu’elle mérite.

0
Ofek Shilon