Comment détecter l'encodage / la page de codes d'un fichier texte

295

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

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

Le detectEncodingFromByteOrderMarks, le StreamReaderconstructeur, travaille pour UTF8 et d' autres fichiers marqués unicode, mais je suis à la recherche d'un moyen de détecter les pages de code, comme ibm850, windows1252.


Merci pour vos réponses, c'est 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 récepteurs sont également des utilisateurs finaux, c'est maintenant ce qu'ils savent des pages de codes: les pages de codes existent et sont ennuyeuses.

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, avec votre intelligence humaine, vous pouvez le deviner.
  • J'ai créé une petite application que l'utilisateur peut utiliser pour ouvrir le fichier et saisir un texte dont l'utilisateur sait qu'il apparaîtra dans le fichier, lorsque la bonne page de code est utilisée.
  • Parcourez toutes les pages de code et affichez celles qui donnent une solution avec le texte fourni par l'utilisateur.
  • Si plusieurs pages de code apparaissent, demandez à l'utilisateur de spécifier plus de texte.
GvS
la source

Réponses:

260

Vous ne pouvez pas détecter la page de code, vous devez le lui dire. Vous pouvez analyser les octets et le 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 Chaque développeur de logiciels doit absolument, positivement, connaître Unicode et les jeux de caractères (pas d'excuses!) .

Plus précisément, Joel dit:

Le fait le plus important sur les codages

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

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

JV.
la source
43
J'ai rétrogradé cette réponse pour deux raisons. Premièrement, dire que «vous devez être informé» n'est pas utile. Qui me le dirait et par quel moyen le feraient-ils? Si c'est moi qui ai enregistré le fichier, à qui devrais-je demander? Moi même? Deuxièmement, l'article n'est pas particulièrement utile comme ressource pour répondre à la question. L'article est plus une histoire d'encodage écrite dans un style David Sedaris. J'apprécie le récit, mais il ne répond pas simplement / directement à la question.
geneorama
9
@geneorama, je pense que l'article de Joel répond mieux que jamais à vos questions, mais voilà ... Le support dépend sûrement de l'environnement dans lequel le texte est reçu. Mieux vaut que le fichier (ou autre) contienne ces informations (je pense au HTML et au XML). Sinon, la personne qui envoie le texte devrait être autorisée à fournir ces informations. Si c'est vous qui avez créé le fichier, comment pouvez-vous ne pas savoir quel encodage il utilise?
JV.
4
@geneorama, suite ... Enfin, je suppose que la principale raison pour laquelle l'article ne répond pas à la question est simplement parce qu'il n'y a pas de réponse simple à cette question. Si la question était "Comment puis-je deviner ...", j'aurais répondu différemment.
JV.
1
@JV J'ai appris plus tard que xml / html peut spécifier l'encodage des caractères, merci d'avoir mentionné cette information utile.
geneorama
1
@JV "Créer un fichier" peut être un mauvais choix de mots. Je suppose qu'un utilisateur peut spécifier l'encodage d'un fichier que l'utilisateur génère. Récemment, j'ai "créé" un fichier à partir d'un cluster Hadoop à l'aide de Hive et je l'ai transmis à un FTP avant de le télécharger sur diverses machines clientes. Le résultat contenait des déchets unicode, mais je ne sais pas quelle étape a créé le problème. Je n'ai jamais spécifié explicitement l'encodage. Je souhaite pouvoir vérifier l'encodage à chaque étape.
geneorama
31

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

Tomer Gabel
la source
9
Curieusement, mon installation de Firefox 3.05 détecte cette page comme UTF-8, montrant un certain nombre de glyphes de point d'interrogation dans un diamant, bien que la source ait une balise META pour Windows-1252. La modification manuelle de l'encodage des caractères affiche correctement le document.
devstuff
5
Votre phrase "Si vous cherchez à détecter des encodages non UTF (c'est-à-dire pas de nomenclature)" est légèrement trompeuse; la norme unicode ne recommande pas d'ajouter une nomenclature aux documents utf-8! (et cette recommandation, ou son absence, est à l'origine de nombreux maux de tête). ref: en.wikipedia.org/wiki/Byte_order_mark#UTF-8
Tao
Cette opération permet de concaténer des chaînes UTF-8 sans accumuler de nomenclatures redondantes. En outre, une marque d'ordre des octets n'est pas nécessaire pour UTF-8, contrairement à UTF-16 par exemple.
sashoalm
26

Avez-vous essayé le port C # pour Mozilla Universal Charset Detector

Exemple 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.");
        }
    }
}    
ITmeze
la source
1
Fonctionne parfaitement pour le type Windows-1252.
seebiscuit
Et comment pouvez-vous l'utiliser pour lire un fichier texte à chaîne en utilisant cela? CharsetDetector retourne le nom de l'encodage au format chaîne et c'est tout ...
Bartosz
@Bartosz private Encoding GetEncodingFromString(string encoding) { try { return Encoding.GetEncoding(encoding); } catch { return Encoding.ASCII; } }
PrivatePyle
15

Vous ne pouvez pas détecter la page de code

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

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

Voici un autre que je viens de trouver en utilisant Google:

shoosh
la source
39
"heuristique" - donc le navigateur ne le détecte pas tout à fait, il fait une supposition éclairée. "ça marche vraiment bien" - alors ça ne marche pas tout le temps alors? Il me semble que nous sommes d'accord.
JV.
10
La norme HTML impose que, si le jeu de caractères n'est pas défini par le document, il doit être considéré comme codé en UTF-8.
Jon Trauntvein
5
Ce qui est cool, sauf si nous lisons des documents HTML non standard. Ou des documents non HTML.
Kos
2
Cette réponse est fausse, j'ai donc dû voter contre. Dire qu'il serait faux que vous ne puissiez pas détecter la page de code, est faux. Vous pouvez deviner et vos suppositions peuvent être plutôt bonnes, mais vous ne pouvez pas "détecter" une page de code.
z80crew
1
@JonTrauntvein Selon les spécifications HTML5 a character encoding declaration is required even if the encoding is US-ASCII - une déclaration manquante se traduit par l'utilisation d'un algorithme heuristique, et non par le repli sur UTF8.
z80crew
9

Je sais qu'il est très tard pour cette question et cette solution ne plaira pas à certains (en raison de son biais centré sur l'anglais et de son manque 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
  • assez fiable (d'après mon expérience) pour les fichiers basés en Europe occidentale contenant des données exotiques (par exemple les noms français) avec un mélange de fichiers de style UTF-8 et latin-1 - essentiellement la majeure partie des environnements américains et européens occidentaux.

Remarque: c'est moi qui ai écrit ce cours, alors prenez-le évidemment avec un grain de sel! :)

Tao
la source
7

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

Hegearon
la source
7

Vous cherchez 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 de codage de base, basée sur 4 premiers octets et probablement 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 le fichier entier.

TarmoPikaro
la source
7

Si quelqu'un cherche 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();
        }
    }
}
Magu
la source
Très belle solution. On peut facilement envelopper le corps de ReadAsString () dans une boucle d'encodages autorisés si plus de 2 encodages (UTF-8 et ASCI 1252) doivent être autorisés.
ViRuSTriNiTy
Après avoir essayé des tonnes d'exemples, je suis enfin arrivé au vôtre. Je suis dans un endroit heureux en ce moment. lol Merci !!!!!!!
Sedrick
Ce n'est peut-être pas la réponse à la façon de détecter 1252 vs 1250, mais cela devrait absolument être la réponse à "Comment détecter UTF-8" avec ou sans nomenclature !!
chuckc
4

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

Étant donné ce dictionnaire (hachage), 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 Traitez-le comme suggéré
  • sinon, prenez un échantillon du texte suffisamment grand, 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 des textes UTF codés qui n'échantillonniez pas commencent par une nomenclature, la deuxième étape couvrira ceux qui a glissé de la première étape.

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

tzot
la source
4

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

Sur ubuntu, vous venez apt-get install uchardet.

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

Erik Aronesty
la source
Sur Mac via homebrew:brew install uchardet
Paul B
3

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

leppie
la source
C'est juste un lien "encodage" ici .. et la description dit que nous devons fournir l'encodage ..
SurajS
@SurajS: Regardez les autres surcharges.
leppie
l'auteur d'origine veut détecter l'encodage d'un fichier, qui ne pourrait pas avoir le marqueur de nomenclature. Le StreamReader détecte le codage de l'en-tête de nomenclature selon la signature. StreamReader public (Stream stream, bool detectEncodingFromByteOrderMarks)
ibondre
1

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

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

C'est GPL v2.

Nick Matteo
la source
0

Vous avez 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 cela;) Fonctionne bien

DeeCee
la source
0

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

La plupart des gens (ou applications) font des choses à peu près dans le même ordre à chaque fois, souvent sur la même machine, il est donc très probable que lorsque Bob crée un fichier .csv et l'envoie à Mary, il utilisera toujours Windows-1252 ou quelle que soit sa machine par défaut.

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

devstuff
la source
0

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

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

J'ai dû 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 ce nouveau 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.

Conseils intrajournaliers
la source
@JohnMachin Je suis d'accord que c'est rare, mais c'est obligatoire par exemple dans certaines parties du protocole IMAP. Si c'est là que vous êtes, vous n'auriez pas à deviner, cependant.
tripleee
0

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

plavozont
la source
0

En tant qu'addon à ITmeze, j'ai utilisé cette fonction pour convertir la sortie du port C # pour Mozilla Universal Charset Detector

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

MSDN

PrivatePyle
la source
0

Merci @ Erik Aronesty d' avoir mentionné uchardet.

Pendant ce temps , l'outil (? Même) existe pour Linux: chardet.
Ou, sur Cygwin vous pouvez utiliser: chardetect.

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

Cela détectera (devinera) heuristiquement l'encodage des caractères pour chaque fichier donné et rapportera le nom et le niveau de confiance pour l'encodage des caractères détecté de chaque fichier.

Schlacki
la source
-1

J'utilise ce code pour détecter la page de code ansi par défaut Unicode et Windows lors de la lecture d'un fichier. Pour les autres codages, une vérification du contenu est nécessaire, manuellement ou par programmation. Cela 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()
Thommy Johansson
la source
-1

10Y (!) S'est écoulé depuis que cela a été demandé, et je ne vois toujours aucune mention de la bonne solution non GPL de MS: 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 a publié la leur, et c'est:

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

C'est 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 de .net. Il y en a d'autres, mais dans l'ensemble, cette bibliothèque n'obtient pas l'attention qu'elle mérite.

Ofek Shilon
la source