Question

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?

Le detectEncodingFromByteOrderMarks , sur le constructeur StreamReader , fonctionne pour UTF8 et d'autres fichiers marqués Unicode, mais je cherche un moyen de détecter les pages de code, 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. C’est maintenant 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, vous le devinerez avec votre intelligence humaine.
  • J'ai créé une petite application que l'utilisateur peut utiliser pour ouvrir le fichier et entrer un texte dont il sait qu'il apparaîtra dans le fichier lorsque la page de code 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.
Était-ce utile?

La solution

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 tout développeur de logiciel doit absolument connaître à propos de Unicode et des jeux de caractères (sans excuses!) .

Plus précisément, Joel dit:

  

Le fait le plus important concernant les codages

     

Si vous oubliez complètement tout ce que je viens d’expliquer, n’oubliez pas 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 " plain " le texte est ASCII.   Il n’existe pas de texte brut.

     

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, sinon vous ne pourrez pas l'interpréter ni l'afficher correctement aux utilisateurs.

Autres conseils

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 consulter le article de Mozilla sur la détection de jeu de caractères universel ( même lien, avec meilleure mise en forme via Wayback Machine ).

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.");
        }
    }
}    
  

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 quant à un codage. Firefox en a un. Vous pouvez télécharger le code et voir comment il le fait. Consultez la documentation ici . Fondamentalement, il s’agit d’une heuristique, mais elle fonctionne très bien.

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

En voici un autre que je viens de découvrir à l'aide de Google:

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 manque de tests statistiques / empiriques), mais cela a très bien fonctionné pour moi, en particulier pour le traitement téléchargé. Données CSV:

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

Avantages:

  • Détection de nomenclature intégrée
  • Encodage par défaut / de secours personnalisable
  • assez 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 Latin-1 - essentiellement la majeure partie 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! :)

Le

Notepad ++ dispose de cette fonctionnalité. Il prend également en charge le changement.

À la recherche d'une solution différente, j'ai constaté 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.

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();
        }
    }
}

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

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

  • s'il 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. ), Je le traite comme suggéré
  • si ce n'est pas le cas, 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 ne commencent pas par une nomenclature, la deuxième étape couvrira ceux qui ont été insérés dans le premier pas.

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

Le constructeur de la classe StreamReader prend un paramètre 'détecter le codage'.

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. Fichiers plus volumineux et plus "typique" les fichiers ont plus de confiance (évidemment).

Sous Ubuntu, vous venez de apt-get install uchardet .

Sur d’autres systèmes, obtenez la source, l’utilisation & amp; docs ici: https://github.com/BYVoid/uchardet

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

  

Enca lit les fichiers de texte donnés ou l’entrée standard quand aucun fichier n’est donné,   et utilise des connaissances sur leur langue (doit être pris en charge par vous) 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.

Vous rencontrez le même problème mais vous n’avez pas encore trouvé de bonne solution pour le détecter automatiquement. Maintenant, j'utilise PsPad (www.pspad.com) pour ça;) ça marche bien

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

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 jamais de mal non plus: -)

En fait, je cherchais un moyen générique de détecter l’encodage du fichier, mais pas la programmation, mais je ne l’avais pas encore trouvé. Ce que j’ai trouvé en testant avec différents encodages, c’est que mon texte était en UTF-7.

Donc, là où je faisais mes premiers pas: StreamReader file = File.OpenText (nom du fichier complet);

Je devais le changer pour: Fichier StreamReader = new StreamReader (nomfichier complet, System.Text.Encoding.UTF7);

OpenText suppose qu'il s'agit du format UTF-8.

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

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

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

Merci @ Erik Aronesty pour avoir mentionné uchardet .

En attendant, le même outil existe pour linux: chardet .
Ou bien, sur cygwin, vous pouvez utiliser: chardetect .

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

Ceci 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.

10Y (!) sont passés depuis que cette question a été posée, et je ne vois toujours aucune mention de la bonne solution de MS non-GPL: API IMultiLanguage2 .

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 est:

  1. Exempt de problèmes de licence GPL et autres,
  2. Sauvegardé et maintenu probablement pour toujours,
  3. Donne une sortie riche - tous les candidats valides pour le codage / 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 bon travail de Carsten Zeumer, qui gère le désordre d’interopérabilité pour l’utilisation du réseau .net. Il en existe d’autres, mais dans l’ensemble, cette bibliothèque n’obtient pas l’attention qu’elle mérite.

J'utilise ce code pour détecter les pages de code ansi par défaut de Windows et Unicode lors de la lecture d'un fichier. Pour les autres codages, une vérification du contenu est nécessaire, manuellement ou par programmation. Ceci peut être utilisé pour enregistrer le texte avec le même encodage que lors de son ouverture. (J'utilise VB.NET)

'Works for Default and unicode (auto detect)
Dim mystreamreader As New StreamReader(LocalFileName, Encoding.Default) 
MyEditTextBox.Text = mystreamreader.ReadToEnd()
Debug.Print(mystreamreader.CurrentEncoding.CodePage) 'Autodetected encoding
mystreamreader.Close()
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top