102 votes

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

Au travail, il semble qu'il ne se passe pas une semaine sans qu'il y ait une connivence, une calamité ou une catastrophe liée au codage. Le problème provient généralement de programmeurs qui pensent pouvoir traiter de manière fiable un fichier "texte" sans spécifier l'encodage. Mais ce n'est pas le cas.

Il a donc été décidé d'interdire dorénavant aux fichiers d'avoir des noms qui se terminent en *.txt ou *.text . L'idée est que ces extensions induisent le programmeur occasionnel en erreur et le conduisent à une complaisance ennuyeuse à l'égard des codages, ce qui entraîne une manipulation inappropriée. Il serait presque préférable de ne pas avoir d'extension du tout, car au moins, vous connaître que tu ne sais pas ce que tu as.

Cependant, nous n'allons pas aller aussi loin. Au lieu de cela, vous devrez utiliser un nom de fichier qui se termine par l'encodage. Ainsi, pour les fichiers texte, par exemple, ce sera quelque chose comme README.ascii , README.latin1 , README.utf8 etc.

Pour les fichiers qui exigent une extension particulière, si l'on peut spécifier l'encodage à l'intérieur du fichier lui-même, comme en Perl ou en Python, il faut le faire. Pour les fichiers tels que les sources Java, pour lesquels il n'existe pas de possibilité de spécifier l'encodage à l'intérieur du fichier, vous placerez l'encodage avant l'extension, comme par exemple SomeClass-utf8.java .

Pour la sortie, UTF-8 doit être fortement préféré.

Mais pour l'entrée, nous devons trouver comment traiter les milliers de fichiers de notre base de code nommés *.txt . Nous voulons tous les renommer pour les adapter à notre nouvelle norme. Mais nous ne pouvons pas tous les examiner à la loupe. Nous avons donc besoin d'une bibliothèque ou d'un programme qui fonctionne réellement.

Ceux-ci se présentent sous diverses formes : ASCII, ISO-8859-1, UTF-8, Microsoft CP1252 ou Apple MacRoman. Bien que nous sachions que nous pouvons dire si quelque chose est en ASCII, et que nous avons de bonnes chances de savoir si quelque chose est probablement en UTF-8, nous restons perplexes quant aux encodages 8 bits. Comme nous travaillons dans un environnement Unix mixte (Solaris, Linux, Darwin) et que la plupart des ordinateurs de bureau sont des Mac, nous avons un certain nombre de fichiers MacRoman gênants. Et ceux-là en particulier posent problème.

Depuis un certain temps, je cherche un moyen de déterminer de manière programmatique quels sont les éléments de la base de données de l'entreprise.

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

et je n'ai pas trouvé de programme ou de bibliothèque capable de distinguer de manière fiable ces trois encodages 8 bits différents. Nous avons probablement plus d'un millier de fichiers MacRoman à eux seuls, donc le détecteur de jeu de caractères que nous utilisons doit être capable de les détecter. Rien de ce que j'ai regardé n'y parvient. J'avais de grands espoirs pour le Bibliothèque de détecteurs de jeux de caractères ICU mais il ne peut pas gérer le MacRoman. J'ai également cherché des modules pour faire le même genre de choses en Perl et en Python, mais c'est toujours la même histoire : pas de support pour détecter le MacRoman.

Je suis donc à la recherche d'une bibliothèque ou d'un programme existant qui détermine de manière fiable dans lequel de ces cinq codages se trouve un fichier - et de préférence plus que cela. En particulier, il doit faire la distinction entre les trois encodages de 3 bits que j'ai cités, notamment MacRoman . Les fichiers sont composés à plus de 99 % de textes en anglais ; il y en a quelques-uns dans d'autres langues, mais pas beaucoup.

S'il s'agit de code de bibliothèque, nous préférons qu'il soit en Perl, C, Java ou Python, et dans cet ordre. S'il s'agit simplement d'un programme, nous ne nous soucions pas vraiment du langage utilisé, du moment qu'il s'agit d'un code source complet, qu'il fonctionne sous Unix et qu'il n'est pas encombré.

Quelqu'un d'autre a-t-il eu ce problème d'un zillion d'anciens fichiers texte encodés de façon aléatoire ? Si oui, comment avez-vous essayé de le résoudre et dans quelle mesure avez-vous réussi ? C'est l'aspect le plus important de ma question, mais je suis également intéressé de savoir si vous pensez qu'encourager les programmeurs à nommer (ou renommer) leurs fichiers avec l'encodage réel de ces fichiers nous aidera à éviter ce problème à l'avenir. Quelqu'un a-t-il déjà essayé de faire respecter cette règle au niveau institutionnel, et si oui, est-ce que cela a été le cas ? que réussi ou non, et pourquoi ?

Et oui, je comprends parfaitement pourquoi on ne peut pas garantir une réponse définitive étant donné la nature du problème. C'est notamment le cas pour les petits fichiers, pour lesquels vous n'avez pas assez de données pour avancer. Heureusement, nos fichiers sont rarement petits. En dehors du hasard README La plupart des fichiers sont d'une taille comprise entre 50 000 et 250 000, mais beaucoup sont plus volumineux. Tout ce qui fait plus de quelques kilomètres est garanti en anglais.

Le domaine du problème est l'exploration de textes biomédicaux, ce qui nous amène parfois à traiter des corpus étendus et extrêmement volumineux, comme l'ensemble du référentiel en libre accès de PubMedCentral. Un fichier assez énorme est le BioThesaurus 6.0, qui pèse 5,7 gigaoctets. Ce fichier est particulièrement gênant car il est presque tous UTF-8. Cependant, une tête de linotte y a collé quelques lignes qui sont dans un encodage 8 bits - Microsoft CP1252, je crois. Il faut un certain temps avant que vous ne trébuchiez sur celui-ci :(

0 votes

Voir stackoverflow.com/questions/4255305/ pour une solution

86voto

dan04 Points 33306

D'abord, les cas faciles :

ASCII

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

UTF-8

Si vos données sont validées comme UTF-8, alors vous pouvez supposer qu'elles sont est UTF-8. En raison des règles de validation strictes d'UTF-8, les faux positifs sont extrêmement rares.

ISO-8859-1 contre Windows-1252

La seule différence entre ces deux encodages est que l'ISO-8859-1 possède les caractères de contrôle C1 là où Windows-1252 possède les caractères imprimables €'ƒ" †‡ˆ‰Š'ŒŽ'""---˜™š'œžŸ. J'ai vu beaucoup de fichiers qui utilisent des guillemets ou des tirets, mais aucun qui utilise des caractères de contrôle C1. Donc ne vous embêtez pas avec eux, ou avec ISO-8859-1, détectez simplement Windows-1252 à la place.

Il ne vous reste plus qu'une seule question.

Comment distinguez-vous le MacRoman du cp1252 ?

C'est beaucoup plus délicat.

Caractères non définis

Les octets 0x81, 0x8D, 0x8F, 0x90, 0x9D ne sont pas utilisés dans Windows-1252. S'ils apparaissent, il faut supposer que les données sont de type MacRoman.

Personnages identiques

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

Approche statistique

Comptez la fréquence des caractères (PAS des octets !) dans les données que vous savez être UTF-8. Déterminez les caractères les plus fréquents. Utilisez ensuite ces données pour déterminer si les caractères cp1252 ou MacRoman sont plus fréquents.

Par exemple, dans une recherche que je viens d'effectuer sur 100 articles aléatoires de Wikipédia en anglais, les caractères non ASCII les plus courants sont les suivants ·•–’èö— . Sur la base de 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 le MacRoman.

Comptez les octets suggérés par le cp1252 et les octets suggérés par le MacRoman, et choisissez celui qui est le plus grand.

6 votes

J'ai accepté votre réponse parce qu'il n'y en avait pas de meilleure et que vous avez fait un bon travail en écrivant sur les questions que je me posais. J'ai en effet des programmes pour renifler ces octets, bien que vous ayez environ le double du nombre que j'avais moi-même trouvé.

10 votes

J'ai finalement réussi à mettre en place ce système. Il s'avère que Wikipedia n'est pas une bonne donnée d'entraînement. A partir de 1k articles aléatoires de en.wikipedia, sans compter la section LANGUES, j'ai obtenu 50k points de code unASCII, mais la distribution n'est pas crédible : le point central et la puce sont trop élevés, &c&c&c. J'ai donc utilisé le corpus PubMed Open Access entièrement UTF8, ce qui a permis d'extraire +14 millions de points de code unASCII. Je les utilise pour construire un modèle de fréquence relative de tous les codages 8 bits, plus sophistiqué que le vôtre mais basé sur cette idée. Cela prouve hautement prédictif de l'encodage pour les textes biomédicaux, le domaine cible. Je devrais le publier. Merci !

5 votes

Je n'ai pas encore de fichiers MacRoman, mais l'utilisation de CR comme délimiteurs de ligne ne constituerait-elle pas un test utile ? Cela fonctionnerait pour les anciennes versions de Mac OS, bien que je ne sache pas pour OS9.

10voto

daxim Points 31874

Mozilla nsUniversalDetector (Liaisons Perl : Encode::Detect / Encode::Detect::Détecteur ) est un million de fois prouvé.

0 votes

Vous trouverez plus de documentation ici : mozilla.org/projets/intl/detectorsrc.html A partir de là, il est suggéré que si vous creusez dans la documentation, vous pouvez trouver les jeux de caractères supportés.

0 votes

@Joel : J'ai creusé dans la source. C'était une question rhétorique. x-mac-cyrillic est prise en charge, x-mac-hebrew est discuté en détail dans les commentaires, x-mac-anything-else n'est pas mentionné.

0 votes

@John Machin : c'est étrange que le cyrillique et l'hébreu soient mentionnés, mais rien d'autre. Je ne faisais que citer une autre source de documentation, je n'avais pas lu plus loin, merci de le faire !

7voto

Michael Borgwardt Points 181658

Ma tentative d'une telle heuristique (en supposant que vous avez exclu l'ASCII et l'UTF-8) :

  • Si les codes 0x7f à 0x9f n'apparaissent pas du tout, il s'agit probablement de l'ISO-8859-1, car ces codes de contrôle sont très rarement utilisés.
  • Si les caractères 0x91 à 0x94 apparaissent souvent, il s'agit probablement de Windows-1252, car ce sont les "guillemets intelligents", de loin les caractères les plus susceptibles d'être utilisés dans un texte anglais. Pour être plus sûr, vous pouvez rechercher des paires.
  • Sinon, c'est du MacRoman, surtout si vous voyez beaucoup de 0xd2 à 0xd5 (c'est là que se trouvent les guillemets typographiques en MacRoman).

Note complémentaire :

Pour les fichiers comme les sources Java, où aucune une telle facilité n'existe pas à l'intérieur du fichier, vous placerez l'encodage avant l'extension, comme par exemple CertainsClass-utf8.java

Ne faites pas ça !

Le compilateur Java s'attend à ce que les noms de fichiers correspondent aux noms de classes, de sorte que le fait de renommer les fichiers rendra le code source impossible à compiler. La bonne solution serait de deviner l'encodage, puis d'utiliser la fonction native2ascii pour convertir tous les caractères non-ASCII en Séquences d'échappement Unicode .

7 votes

Stoopid kompilor ! Non, nous ne pouvons pas dire aux gens qu'ils ne peuvent utiliser que l'ASCII ; ce n'est plus les années 60. Ce ne serait pas un problème s'il y avait une annotation @encoding pour que le fait que la source soit dans un encodage particulier ne soit pas obligé d'être stocké en dehors du code source, un défaut vraiment idiot de Java dont ni Perl ni Python ne souffrent. Cela devrait être dans la source. Mais ce n'est pas notre principal problème, ce sont les milliers de fichiers *.text des fichiers.

3 votes

@tchrist : Il ne serait en fait pas si difficile d'écrire votre propre processeur d'annotation pour supporter une telle annotation. C'est quand même un oubli embarrassant de ne pas l'avoir dans l'API standard.

0 votes

Même si Java supportait @encoding, cela ne garantirait pas que la déclaration d'encodage soit correct .

6voto

John Machin Points 39706

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

"nous avons une bonne chance de savoir si quelque chose est probablement UTF-8" : En fait, la probabilité qu'un fichier contenant du texte significatif encodé dans un autre jeu de caractères qui utilise des octets à nombre de bits élevé soit décodé avec succès en UTF-8 est extrêmement faible.

Stratégies UTF-8 (dans la langue la moins préférée) :

# 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 jeux de caractères d'origine Mozilla dont j'ai connaissance ne supportent pas le MacRoman et, de toute façon, ne font pas un bon travail sur les jeux de caractères 8 bits, en particulier avec l'anglais, car ils dépendent de la vérification du sens du décodage dans la langue donnée, en ignorant les caractères de ponctuation, et en se basant sur une large sélection de documents dans cette langue.

Comme d'autres l'ont fait remarquer, vous ne disposez vraiment que des caractères de ponctuation à haut niveau de bit pour faire la distinction entre cp1252 et macroman. Je vous suggère d'entraîner un modèle de type Mozilla sur vos propres documents, et non sur Shakespeare, le Hansard ou la Bible KJV, et de prendre en compte les 256 octets. Je présume que vos fichiers ne contiennent pas de balises (HTML, XML, etc.), ce qui fausserait les probabilités de manière choquante.

Vous avez mentionné des fichiers qui sont principalement UTF-8 mais qui ne parviennent pas à être décodés. Vous devriez également vous méfier de :

(1) les fichiers qui sont prétendument codés en ISO-8859-1 mais qui contiennent des "caractères de contrôle" dans la plage 0x80 à 0x9F inclus ... ceci est tellement répandu que le projet de norme HTML5 dit qu'il faut décoder TOUTES Les flux HTML sont déclarés comme ISO-8859-1 en utilisant cp1252.

(2) les fichiers qui se décodent correctement en UTF-8 mais dont l'Unicode résultant contient des "caractères de contrôle" dans la plage U+0080 à U+009F inclus ... cela peut résulter du transcodage de fichiers cp1252 / cp850 (cela s'est vu !) / etc. de "ISO-8859-1" à UTF-8.

Le contexte : J'ai le projet de créer un détecteur de jeux de caractères basé sur Python, orienté fichiers (plutôt que Web) et fonctionnant bien avec les jeux de caractères 8 bits, dont les suivants legacy ** n comme le cp850 et le cp437. Ce n'est pas encore l'heure du prime time. Je suis intéressé par les fichiers d'entraînement ; vos fichiers ISO-8859-1 / cp1252 / MacRoman sont-ils aussi "non encombrés" que la solution de code de quiconque ?

1 votes

La raison de l'ordre des langues est l'environnement. La plupart de nos applications majeures tendent à être en java et les utilitaires mineurs et certaines applications sont en perl. Nous avons un peu de code ici et là qui est en python. Je suis principalement un programmeur C et Perl, du moins par choix, donc je cherchais soit une solution java à brancher sur notre bibliothèque d'applications, soit une bibliothèque Perl pour la même chose. En C, je pourrais construire une couche de colle XS pour la connecter à l'interface perl, mais je n'ai jamais fait cela en python auparavant.

3voto

Epcylon Points 2548

Comme vous l'avez découvert, il n'existe pas de moyen parfait de résoudre ce problème, car sans la connaissance implicite de l'encodage utilisé par un fichier, tous les encodages 8 bits sont exactement les mêmes : une collection d'octets. Tous les octets sont valables pour tous les encodages 8 bits.

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

D'un autre côté, si vous savez que le texte d'un fichier est écrit en anglais, il est peu probable que vous remarquiez une différence quel que soit l'encodage que vous décidez d'utiliser pour ce fichier, car les différences entre tous les encodages mentionnés sont toutes localisées dans les parties des encodages qui spécifient les 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 la ponctuation (CP1252 a plusieurs versions des caractères de citation par exemple), mais pour l'essentiel du texte, il n'y aura probablement aucun problème.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X