Comment deviner de manière fiable l'encodage entre MacRoman, CP1252, Latin1, UTF-8, et ASCII

au travail, il semble qu'aucune semaine ne passe sans conniption, calamité ou catastrophe liée à l'encodage. Le problème vient généralement des programmeurs qui pensent pouvoir traiter de manière fiable un fichier "texte" sans spécifier l'encodage. Mais tu ne peux pas.

il a donc été décidé d'interdire désormais aux fichiers d'avoir des noms qui se terminent par *.txt ou *.text . La pensée est que ces extensions induisent le programmeur occasionnel en erreur dans un terne la complaisance à l'égard des encodages, ce qui entraîne une mauvaise manipulation. Ce serait presque mieux de ne pas extension du tout, parce qu'au moins alors vous savoir que vous ne savez pas ce que vous avez.

Cependant, nous ne sommes pas allés si loin. À la place, vous devrez utiliser un nom de fichier qui se termine par l'encodage. Donc pour les fichiers texte, par exemple, ce serait quelque chose comme README.ascii , README.latin1 , README.utf8 , etc.

pour les fichiers qui demandent une extension particulière, si l'on peut spécifier l'encodage à l'intérieur du fichier lui-même, comme dans Perl ou Python, alors vous devez le faire. Pour les fichiers comme Java source où aucune facilité de ce type n'existe à l'intérieur du fichier, vous mettrez l'encodage avant l'extension, comme SomeClass-utf8.java .

pour la sortie, UTF-8 doit être fortement préférable.

mais pour la contribution, nous avons besoin de comprendre comment traiter avec les milliers de fichiers dans notre base de données nommée *.txt . Nous voulons tous les renommer pour les adapter à notre nouvelle norme. Mais on ne peut pas le globe oculaire de tous. Nous avons donc besoin d'une bibliothèque ou d'un programme qui fonctionne réellement.

ceux-ci sont diversement en ASCII, ISO-8859-1, UTF-8, Microsoft CP1252, ou Apple MacRoman. Bien que nous sachions que nous pouvons dire si quelque chose est ASCII, et que nous supportons un bon changement de savoir si quelque chose est probablement UTF-8, nous sommes perplexes à propos des encodages 8 bits. Parce que nous fonctionnons dans un environnement Unix mixte (Solaris, Linux, Darwin) avec la plupart des ordinateurs de bureau étant Macs, nous avons tout à fait quelques fichiers MacRoman ennuyeux. Et ces résultats sont un problème.

depuis un certain temps maintenant je cherche un moyen de déterminer programmatically lequel de

  1. ASCII
  2. ISO-8859-1
  3. CP1252
  4. MacRoman
  5. UTF-8

un fichier est dans, et je n'ai pas trouvé un programme ou une bibliothèque qui peut distinguer de manière fiable entre ceux-ci les trois différents encodages 8 bits. Nous avons probablement plus d'un millier de fichiers MacRoman seuls, donc quel que soit le détecteur de charset que nous utilisons, il doit être capable de les renifler. Rien de ce que j'ai vu ne peut y arriver. J'avais de grands espoirs pour le ICU charset detector library , mais il ne peut pas gérer MacRoman. J'ai aussi regardé des modules à faire le même genre de chose dans Perl et Python, mais encore et encore c'est toujours la même histoire: pas de support pour détecter MacRoman.

ce que je cherche donc, c'est une bibliothèque ou un programme existant qui détermine de manière fiable dans lequel de ces cinq encodages se trouve un fichier-et de préférence plus que cela. En particulier, il doit distinguer entre les trois encodage à 3 bits que j'ai cité, surtout MacRoman . Les fichiers sont plus de 99% anglais il y en a quelques-unes dans d'autres langues, mais pas beaucoup.

si c'est du code de Bibliothèque, Notre préférence linguistique est Qu'il soit en Perl, C, Java, ou Python, et dans cet ordre. Si ce n'est qu'un programme, alors nous ne nous soucions pas vraiment de la langue dans laquelle il est à condition qu'il vienne en source complète, fonctionne sur Unix, et est entièrement non grevée.

est-ce que quelqu'un d'autre a eu ce problème d'un zillion de fichiers textes hérités encodés au hasard? Si oui, comment avez-vous essayé de résoudre ça, et à quel point Avez-vous réussi? C'est l'aspect le plus important de ma question, mais je suis également intéressé à savoir si vous pensez que d'encourager les programmeurs à nommer (ou renommer) leurs fichiers avec le codage réel de ces fichiers sont dans nous aidera à éviter le problème à l'avenir. Quelqu'un a déjà essayé d'appliquer ce sur une base institutionnelle, et si oui, était que réussi ou non, et pourquoi?

et oui, je comprends parfaitement pourquoi on ne peut pas garantir une réponse définitive, compte tenu de la nature du problème. C'est particulièrement le cas avec les petits fichiers, où vous n'avez pas assez de données pour aller sur. Heureusement, nos dossiers sont rarement petits. Mis à part le fichier aléatoire README , la plupart sont dans la gamme de taille de 50k à 250k, et beaucoup sont plus grands. Tout ce qui dépasse quelques K en taille est garanti en anglais.

le domaine de problème est l'exploration de textes biomédicaux, donc nous traitons parfois avec de vastes et extrêmement grandes entreprises, comme tous les respositaires à libre accès de PubMedCentral. Un fichier assez énorme est le BioThesaurus 6.0, à 5.7 gigaoctets. Ce fichier est particulièrement ennuyeux car il est presque tout UTF-8. Cependant, certains numbskull est allé et a collé quelques lignes qui sont en quelque 8-bits-Microsoft CP1252, je crois. Il prend un certain temps avant de voyage. : (

96
demandé sur Jonas 2010-11-16 23:50:46

7 réponses

D'abord, les cas faciles:

ASCII

si vos données ne contiennent pas d'octets au-dessus de 0x7F, alors C'est ASCII. (Ou un encodage ISO646 7 bits, mais ceux-ci sont très obsolètes.)

UTF-8

si vos données sont validées en tant qu'UTF-8, alors vous pouvez en toute sécurité supposer que est UTF-8. En raison des règles de validation strictes de L'UTF-8, les faux positifs sont extrêmement rares.

ISO-8859-1 vs. windows-1252

la seule différence entre ces deux codages est que la norme ISO-8859-1 a les caractères de contrôle C1 où windows-1252 a les caractères imprimables €'"...†‡ˆ‰Š"ŒŽ"""•--™š " œžŸ. J'ai vu beaucoup de fichiers qui utilisent des guillemets ou des tirets, mais aucun qui utilisent C1 caractères de contrôle. Alors ne vous en souciez même pas, ou ISO-8859-1, détectez juste windows-1252 à la place.

qui ne vous laisse plus qu'une question.

Comment distinguer MacRoman de cp1252?

C'est beaucoup plus compliqué.

caractères non définis

les octets 0x81, 0x8D, 0x8F, 0x90, 0x9D ne sont pas utilisés dans windows-1252. S'ils se produisent, alors supposer que les données sont MacRoman.

caractères identiques

les octets 0xA2 ( ¢ ), 0xA3 ( £ ), 0xA9 ( © ), 0xB1 ( ± ), 0xB5 (µ) sont les mêmes dans les deux encodages. Si ceux-ci sont les seuls non-ASCII octets, alors il n'a pas d'importance si vous choisissez MacRoman ou cp1252.

approche statistique

Nombre de caractères (PAS d'octets!) fréquences dans les données que vous savez être UTF-8. Déterminez les caractères les plus fréquents. Ensuite, utilisez ces données pour déterminer si les caractères cp1252 ou MacRoman sont plus communs.

par exemple, dans une recherche que je viens d'effectuer sur 100 articles Wikipédia anglais aléatoires, les caractères non-ASCII les plus communs sont ·•–é°®’èö— . Basé sur ce fait,

  • les octets 0x92, 0x95, 0x96, 0x97, 0xAE, 0xB0, 0xB7, 0xE8, 0xE9, ou 0xF6 suggèrent windows-1252.
  • les octets 0x8E, 0x8F, 0x9A, 0xA1, 0xA5, 0xA8, 0xD0, 0xD1, 0xD5, ou 0xE1 suggèrent MacRoman.

comptez les octets suggérant cp1252 et les octets suggérant MacRoman, et allez avec celui qui est le plus grand.

85
répondu dan04 2010-11-17 01:38:46
10
répondu daxim 2010-11-16 23:26:16

ma tentative d'un tel heuristique (en supposant que vous avez exclu ASCII et UTF-8):

  • si 0x7f à 0x9f n'apparaît pas du tout, c'est probablement ISO-8859-1, car ce sont des codes de contrôle très rarement utilisés.
  • Si 0x91 par 0x94 apparaissent à beaucoup, c'est probablement Windows-1252, parce que ce sont les "guillemets", de loin les plus susceptibles de caractères dans ce domaine à être utilisé dans un texte en anglais. Pour être plus sûr, vous pourriez chercher des paires.
  • sinon, C'est MacRoman, surtout si vous voyez beaucoup de 0xd2 à 0xd5 (c'est là que les citations typographiques sont en MacRoman).

note de Côté:

pour les fichiers comme Java source où aucun cette facilité existe à l'intérieur de la fichier, vous allez mettre le codage avant l'extension, telle que SomeClass-utf8.java

Ne faites pas ça!!

le compilateur Java s'attend à ce que les noms de fichiers concordent avec les noms de classe. La bonne chose serait de deviner l'encodage, puis utiliser le native2ascii outil pour convertir tous les caractères non-ASCII en séquences D'échappement Unicode .

7
répondu Michael Borgwardt 2017-04-05 15:56:52

"Perl, C, Java ou Python, et dans l'ordre": attitude intéressante :-)

" we stand a good change of knowing if something is probably UTF-8": en fait, la chance qu'un fichier contenant du texte significatif encodé dans un autre jeu de caractères qui utilise des octets high-bit-set va décoder avec succès comme UTF-8 est en voie de disparition petite.

UTF-8 stratégies (à moins langue de préférence):

# 100% Unicode-standard-compliant UTF-8
def utf8_strict(text):
    try:
        text.decode('utf8')
        return True
    except UnicodeDecodeError:
        return False

# looking for almost all UTF-8 with some junk
def utf8_replace(text):
    utext = text.decode('utf8', 'replace')
    dodgy_count = utext.count(u'\uFFFD') 
    return dodgy_count, utext
    # further action depends on how large dodgy_count / float(len(utext)) is

# checking for UTF-8 structure but non-compliant
# e.g. encoded surrogates, not minimal length, more than 4 bytes:
# Can be done with a regex, if you need it

une fois que vous avez décidé que ce n'est ni ASCII ni UTF-8:

les détecteurs de charset D'origine Mozilla que je connais ne supportent pas MacRoman et de toute façon ne font pas un bon travail sur les charsets 8-bits surtout avec l'anglais parce QU'AFAICT ils dépendent de vérifier si le décodage a du sens dans la langue donnée, ignorant les caractères de ponctuation, et basé sur un large choix de documents dans cette langue.

comme d'autres l'ont fait remarquer, vous n'avez vraiment que le high-bit-set les caractères de ponctuation disponibles permettent de distinguer cp1252 et macroman. Je suggère de former un modèle de type Mozilla sur vos propres documents, pas sur Shakespeare ou Hansard ou la Bible de KJV, et en tenant compte des 256 octets. Je présume que vos fichiers n'ont pas de markup (HTML, XML, etc.) en eux -- ce qui déformerait les probabilités quelque chose de choquant.

vous avez mentionné des fichiers qui sont principalement UTF-8 mais ne parviennent pas à décoder. Vous devriez aussi être très méfiant de:

(1) fichiers qui seraient encodés dans la norme ISO-8859-1 mais qui contiennent des" caractères de contrôle " dans la plage de 0x80 à 0x9F inclusivement ... c'est si courant que le projet de norme HTML5 dit de décoder tous les flux HTML déclarés ISO-8859-1 en utilisant cp1252.

(2) fichiers qui décodent OK comme UTF-8 mais L'Unicode résultant contient des" caractères de contrôle " dans la gamme U+0080 à U+009F inclus ... cela peut résulter du transcodage cp1252 / cp850 (voir il se!) / etc fichiers à partir de "ISO-8859-1 à UTF-8.

Background: j'ai un projet wet-Sunday-afternoon pour créer un détecteur de charset basé sur Python qui est orienté fichier (au lieu de web-oriented) et fonctionne bien avec des jeux de caractères 8-bit dont legacy ** n ceux comme cp850 et cp437. C'est loin d'être le premier instant. Je suis intéressé par les dossiers de formation; sont vos ISO-8859-1 / cp1252 / MacRoman aussi "non encombré" que la solution de code de n'importe qui?

5
répondu John Machin 2010-11-17 00:09:18

comme vous l'avez découvert, il n'y a pas de façon parfaite de résoudre ce problème, car sans la connaissance implicite sur laquelle encoder un fichier utilise, tous les encodages 8 bits sont exactement les mêmes: une collection d'octets. Tous les octets sont valides pour tous les encodages à 8 bits.

Le mieux que vous pouvez espérer, c'est une sorte d'algorithme qui analyse les octets, et basé sur les probabilités d'un certain octet utilisé dans une certaine langue avec un certain encodage va deviner à quel l'encodage des fichiers. Mais cela doit savoir quelle langue le fichier utilise, et devient complètement inutile lorsque vous avez des fichiers avec des encodages mixtes.

à l'envers, Si vous savez que le texte d'un fichier est écrit en anglais, alors il est peu probable de remarquer une différence quelle que soit l'encodage que vous décidez d'utiliser pour ce fichier, car les différences entre tous les encodages mentionnés sont tous localisés dans les parties des encodages qui spécifient des caractères qui ne sont pas normalement utilisés dans la langue anglaise. Vous pourriez avoir quelques problèmes lorsque le texte utilise un formatage spécial, ou des versions spéciales de ponctuation (CP1252 a plusieurs versions des caractères de citation par exemple), mais pour l'essentiel du texte il n'y aura probablement pas de problèmes.

3
répondu Epcylon 2010-11-16 21:18:13

si vous pouvez détecter tous les encodages sauf macroman, qu'il serait logique de supposer que ceux qui ne peuvent pas être déchiffrés sont en macroman. En d'autres termes, il suffit de faire une liste des fichiers qui ne pouvaient pas être traités et de gérer ceux-ci comme s'ils étaient macroman.

une Autre façon de trier ces fichiers serait de faire un serveur qui permet aux utilisateurs de décider lequel l'encodage n'est pas déformé. Bien sûr, ce serait au sein de l'entreprise, mais avec 100 employés en faisant quelques-uns chaque jour, vous aurez des milliers de dossiers faits en un rien de temps.

enfin, ne serait-il pas préférable de simplement convertir tous les fichiers existants dans un seul format, et exiger que les nouveaux fichiers soient dans ce format.

1
répondu Eric Pauley 2010-11-16 21:18:32

est-ce que quelqu'un d'autre a eu ce problème d'un zillion de fichiers textes hérités encodés au hasard? Si oui, comment avez-vous tenté de le résoudre, et dans quelle mesure avez-vous réussi?

je suis en train d'écrire un programme qui traduit des fichiers en XML. Il a pour détecter automatiquement le type de chaque fichier, qui est un sur-ensemble du problème de la détermination de l'encodage d'un fichier texte. Pour déterminer l'encodage, j'utilise une approche bayésienne. C'est, de mon classement le code calcule une probabilité (vraisemblance) qu'un fichier texte ait un encodage particulier pour tous les encodages qu'il comprend. Ensuite, le programme sélectionne les plus probables décodeur. L'approche bayésienne fonctionne ainsi pour chaque encodage.

  1. définit la probabilité initiale ( avant ) que le fichier est dans l'encodage, basé sur les fréquences de chaque encodage.
  2. Examiner chaque octet dans le fichier. Look-up de l'octet valeur pour déterminer la corrélation entre cette valeur de byte étant présent et un fichier étant effectivement dans cet encodage. Utilisez cette corrélation pour calculer une nouvelle ( postérieure ) probabilité que le fichier est dans l'encodage. Si vous avez plus d'octets à examiner, utilisez la probabilité postérieure de cet octet comme la probabilité préalable lorsque vous examinez le prochain octet.
  3. quand vous arrivez à la fin du fichier (je ne regarde en fait que les 1024 premiers octets), le proability vous avez est la probabilité que le fichier est dans l'encodage.

il s'avère que le théorème de Bayes devient très facile à faire si au lieu de calculer des probabilités, vous calculez information content , qui est le logarithme du odds : info = log(p / (1.0 - p)) .

vous devrez calculer la probabilité initail priori, et les corrélations, en examinant un corpus de fichiers que vous avez classifiés manuellement.

1
répondu Raedwald 2012-02-10 13:55:26