Sortie de uchardet 0.0.8 pour la détection de codages de caractères

Posté par  (site web personnel, Mastodon) . Édité par Julien Jorge et Benoît Sibaud. Modéré par Benoît Sibaud. Licence CC By‑SA.
63
21
déc.
2022
C et C++

uchardet est une bibliothèque C/C++ de détection de codage de caractère (par exemple UTF-8 ou ISO-8859-15 sont ce qu’on appelle des « codages de caractères » ou « jeux de caractères ») basée sur des caractéristiques statistiques des langages naturels et membre du projet Freedesktop. Elle peut détecter quelques dizaines de codages de caractères.

Le projet fournit également un outil en ligne de commande pour tester très simplement le codage de vos fichiers ou de flux de texte.

uchardet version 0.0.8 est sortie ce 8 décembre 2022.

Sommaire

Usage

Outil en ligne de commande

L’outil en ligne de commande n’est nullement le point principal du projet, mais reste fort utile si vous avez un fichier texte dont vous n’êtes pas sûr du codage:

$ uchardet mon-fichier.txt
ISO-8859-15

Si vous inspectez plusieurs fichiers à la fois, la sortie contiendra une ligne par fichier, préfixée du nom de fichier:

$ uchardet test/fr/iso-8859-15.txt test/de/windows-1252.txt
test/fr/iso-8859-15.txt: ISO-8859-15
test/de/windows-1252.txt: WINDOWS-1252

Accessoirement vous pouvez l’utiliser en mode pipe pour récupérer un flux de données. Pour prendre un exemple réaliste, regardons la page web archivée qui explique l’algorithme à l’origine de cet outil (j’en parle dans l’historique). Très ironiquement, cette page s’affiche avec des caractères de remplacement '�' dans Firefox, ce qui indique une erreur de décodage (pour qui n’a pas compris en quoi c’est ironique : ce projet était originellement fait par Mozilla pour Firefox, mais il n’est plus inclus depuis de nombreuses années et maintenant même la page en parlant est mal affichée).

Screenshot of the algorithm paper for uchardet original algorithm with broken encoding in Firefox

La raison est que le serveur web lui-même envoie un en-tête content-type: text/html; charset=UTF-8, même si la page web contient bien un charset=windows-1252 en métadonnées. Firefox semble privilégier l’en-tête envoyé par le serveur.

Supposons néanmoins que nous n’ayons aucune métadonnée (ni côté serveur, ni dans le code HTML) et que nous voulions deviner le jeu de caractères:

$ curl https://www-archive.mozilla.org/projects/intl/universalcharsetdetection | uchardet
WINDOWS-1252

Et voilà! Notons qu’idéalement, vous devriez même retirer tout texte structurel qui pourrait interférer avec la détection (j’en reparlerai dans la section sur les principes de détection. Par exemple pour retirer les tags HTML sur une seule ligne:

$ curl https://www-archive.mozilla.org/projects/intl/universalcharsetdetection | sed 's/<[^>]*>//g' | uchardet
WINDOWS-1252

Cela peut sembler inutile ici puisque l’outil a eu bon dès le début, mais ne pas en prendre l’habitude peut aisément créer des faux positifs.

Note: il s’agit d’un « nettoyage » simpliste d’un source HTML, pour démonstration. Notamment avec les nouvelles API de détection de langage (voir la section « Futur: API de détection de langage »), j’obtiens parfois des erreurs de détection de langage si je prends une page Wikipédia sans bien la nettoyer, car il y a beaucoup de contenu structurel dans le code des pages Wikipedia.

Notons que les noms de codage sont compatibles avec iconv. Par conséquent vous pouvez très aisément utiliser le résultat directement pour convertir dans un codage qui vous convient mieux, si nécessaire. Par exemple, maintenant que vous savez que le texte est en WINDOWS-1252, transformons le en UTF-8:

$ curl https://www-archive.mozilla.org/projects/intl/universalcharsetdetection | iconv -f WINDOWS-1252 -t UTF-8

Interface de programmation

L’API d’uchardet est en C/C++ (implémentation en C++ mais interfacée pour être aisément incluse dans du code C ou C++) et extrêmement simple : vous créez un objet uchardet_t, vous l’alimentez en données textuelles (en une fois ou par lots), puis vous obtenez le codage de caractère au format texte, compatible avec iconv:

#include <uchardet.h>

const char *
print_charset(const char* data,
              size_t      data_len)
{
  const char *charset
  uchardet_t  handle;

  handle = uchardet_new();

  uchardet_handle_data(handle, data, data_len);
  uchardet_data_end(handle);
  charset = uchardet_get_charset(handle);

  printf("%s\n », charset);
  uchardet_delete(handle);
}

Notons que l’un des grands avantages d’uchardet est sa rapidité exemplaire. C’est un système basé sur des modèles statistiques de langage, générés sous forme de code C++, puis compilés, qui est capable de déterminer le codage d’un fichier très rapidement, car basé sur des logiques très simples mathématiquement.

Un peu d’histoire

Comme uchardet est très lié à ma propre expérience où je voulais originellement simplement satisfaire un besoin personnel, et que je me suis retrouvé à maintenir des années une bibliothèque, je me suis dit que c’est une petite histoire marrante à raconter.

2002-2011: code universalchardet chez Mozilla

Le projet est né de Mozilla, il y a une vingtaine d’années (en tous cas, le papier est daté de 2002), sous le nom de universalchardet. Il me semble que le code fut même dans leur navigateur pendant plusieurs années.

Malheureusement je ne retrouve plus d’archive du code de l’époque (on trouve en fait la structure du code archivé par le projet archive.org mais le contenu des fichiers source n’est pas archivé). Seul le papier de recherche qui explique les bases des algorithmes utilisés est encore disponible.

2011-2015: intégration dans une bibliothèque dédiée

Néanmoins si quelqu’un est intéressé par le code d’origine, le premier commit de uchardet en 2011 donne probablement un bon aperçu de ce à quoi ressemblait les dernières versions de Mozilla. À l’époque, le code était encore accessible sur les dépôts de Mozilla, mais il ne pouvait être aisément utilisé par d’autres projets, hormis en le copiant manuellement. BYVoid, le mainteneur originel de cette bibliothèque a donc extrait le code des dépôts Mozilla pour créer l’interface que j’ai montrée plus haut.

C’est ainsi que pendant quelques années, de 2011 à 2015, il y eut un peu de nettoyage et maintenance par BYVoid et quelques autres contributeurs. Pendant cette période, on compte un peu moins d’une trentaine de commits, et on peut considérer que ce fut principalement de la maintenance sans modification profonde du code originel.

2015: j’ai besoin de lire des sous-titres !

Les sous-titres de films furent mon entrée dans ce projet. Comme la plupart des occidentaux utilisant un alphabet avec des accents, j’avais déjà remarqué que les sous-titres de film étaient très souvent cassés. Quiconque a déjà vu ce genre de sous-titre sait de quoi je parle:

Luke, je suis ton p�re!

Voire:

Luke, je suis ton pÚre!

😅

Les jeunes générations ont peut-être moins ce problème, maintenant que UTF-8 est vraiment mis en avant, mais il y a encore peu, beaucoup de textes en français étaient codés en ISO-8859-1 ou ISO-8859-15 (il peut aussi y avoir des textes français en WINDOWS-1252 notamment).

Comme tout le monde avant moi, j’ai fait abstraction de ce problème pendant une décennie (puisque cela restait lisible avec de l’imagination). Sauf que le jour où j’ai donné des sous-titres en japonais ou coréen à mplayer, mpv, VLC, ou tout autre lecteur (j’ai vu ce problème même sous Windows, pour quiconque croirait que c’est juste sous Linux), on se retrouvait avec du texte absolument illisible (et ce quel que soit votre pouvoir d’imagination !). La solution était d’essayer tous les codages des listes proposées dans l’interface des lecteurs vidéos (des dizaines, car les gens sont rarement au courant des jeux de caractères habituels pour une langue, donc on les essaie tous). Même si à force, on peut mieux connaître les codages les plus “habituels” d’un langage cible, cela reste un processus frustrant et fastidieux.

Je regardai donc le code de mpv (fork de mplayer, excellent lecteur de vidéo) et vit qu’il utilisait enca pour détecter le codage des fichiers de sous-titre. Malheureusement son efficacité était très limitée (notamment le coréen et le japonais n’étaient pas pris en charge), et surtout sans donner un indice (hint) à enca, il est incapable de déterminer un codage mono-octet (c’est-à-dire notamment tous les ISO-8859-* et comme je disais, le français était souvent codé ainsi, ce qui expliquait les erreurs de décodage constantes). Cette très forte limitation s’expliquera très facilement quand vous lirez la section sur les principes de détection, plus bas.

Je me penchai alors sur l’état des lieux des bibliothèques de détection de codages de caractère. À l’époque, une autre bibliothèque libre ressortait dans toutes les recherches web : libguess (qui disait reconnaître plus de langues). Je la teste pour rapidement découvrir que le hinting est aussi obligatoire (ce que je confirme avec le code). Et encore, en essayant un fichier EUC-KR (le codage le plus commun en Corée), avec "korean" en indice, la détection foirait. Ce n’est pas une validation très scientifique, mais comme les langues asiatiques étaient mes cibles principales (et européennes en secondaire), c’était mal parti.

C’est alors que je découvris uchardet. Il n’était pas parfait, mais clairement il marchait bien mieux (mes tests étaient très encourageants), et surtout ne nécessitait pas de rajouter des “indices” de langue pour être efficace.

En cette année 2015, je poussai donc à son intégration dans mpv, puis je contribuai même un petit patch additionnel pour en faire le détecteur par défaut (enca restait en second choix ; il est intéressant de noter que la prise en charge de ce dernier a été retirée totalement depuis).

Comme j’avais poussé à son usage, et que la maintenance de uchardet n’est pas au plus haut, je sentis le besoin de patcher des bugs. C’est ainsi que je fis mon premier patch dans uchardet cette même année.

Aparté : c’est aussi un petit rappel pour ceux qui croient que l’informatique devrait être en ASCII seulement, et que c’est suffisant. La réalité est que nous ne sommes pas seuls sur terre, et pour avoir dû batailler pendant de nombreuses années avec nos systèmes libres pour simplement pouvoir lire du texte dans certaines autres langues (d’où mes contributions dans uchardet, mpv et d’autres projets), ou pour écrire dans ces langages (côté écriture, j’ai d’ailleurs contribué au moins un patch dans ibus-hangul ! Chaque usage cassé son patch ! 😩), je peux affirmer sans une once d’hésitation que nos systèmes libres ne sont pas si accueillants aux non-occidentaux. Cela s’améliore, mais c’est encore loin d’être parfait et sans pour autant dire aux développeurs bénévoles qu’ils devraient eux-mêmes implémenter des fonctionnalités dédiées, il faut être ouvert aux patchs qui améliorent l’internationalisation de votre logiciel (ce principe ne s’appliquant pas qu’aux textes affichés, mais aussi à l’interface et à la possibilité d’écrire du texte).
Il est important de reconnaître au moins que ces usages existent et qu’un logiciel (ou internet) n’est pas fait pour être ASCII seulement !

2015-2022 : maintenance et (dé)cadence

Quoi qu’il en soit, c’est ainsi que je commençai à contribuer plus à uchardet puis en devint le co-mainteneur (puis rapidement le seul mainteneur puisque le projet était déjà en mode de maintenance minimale à l’époque).
J’ai immédiatement vu le potentiel du projet et c’est à ce moment-là que le projet a commencé à évoluer :

  • Rendre les valeurs de retour iconv-compatible pour un interfaçage aisé (iconv étant le projet le plus commun pour de la conversion entre codages de caractères) dans des projets tiers.
  • Ajouter des tests unitaires pour chaque combinaison de langage et de codage officiellement pris en charge et une infrastructure de tests automatiques pour éviter les régressions.
  • Créer des scripts de génération de modèles de langage par extraction et traitement de données Wikipédia. C’était un changement majeur, car il permettait de facilement générer et regénérer des modèles pour détecter des langues et des codages.
  • En conséquence du point précédent, de nombreuses prises en charge ont été très aisément ajoutées à uchardet qui reconnaît maintenant beaucoup plus de langues et de codages qu’à ses origines.
  • Améliorer l’algorithme de “confiance” de détection, pour des résultats bien plus fiables qu’à l’origine.
  • etc.

J’en fis aussi un projet Freedesktop assez rapidement puisque je compris qu’il y avait un réel manque dans le libre (les 2 autres alternatives ne marchant pas de manière satisfaisante) et que c’était donc un projet utile pour un bureau libre. En outre, c’était mieux ainsi, plutôt que sur le compte du précédent mainteneur (qui était d’accord pour ce changement bien sûr) sur une plateforme de gestion de source propriétaire (c’était en effet initialement publié sur un compte personnel sur Github).

Comparaison avec d’autres projets en 2022

De nos jours, le « paysage logiciel » est un peu différent. D’un certain côté, ce type de détection est devenu bien moins nécessaire, car les formats ont souvent des métadonnées associées pour en indiquer le jeu de caractère. Sur le web par exemple, il est recommandé dorénavant de déclarer ce dernier alors que c’était plus chaotique, il y a une vingtaine d’années. Je n’ai pas d’information interne, mais j’ai toujours imaginé que c’était la raison pour laquelle Mozilla avait abandonné son propre code de détection (l’origine de uchardet donc). Je suppose qu’à un moment, ils se sont dit que ce n’était plus nécessaire lorsque les documents web sont devenus globalement bien “étiquetés”.

En outre, UTF-8 a vraiment pris ses marques et est devenu lui-même de plus en plus le jeu recommandé dans une majorité d’usages, diminuant d’autant les chances de tomber sur d’autres codages de caractères sur des fichiers récents. D’ailleurs certains formats de fichiers ou protocoles récents ont fait le choix de rendre UTF-8 obligatoire (c’est le cas de XMPP dès les premières RFC par exemple).

Néanmoins les vieux fichiers (archives, etc.) existent encore, les gens s’échangent encore parfois de simples fichiers textes sans métadonnées. D’ailleurs les formats de fichiers de sous-titre sont encore souvent de bêtes fichiers textes avec une structure très basique (par exemple les fichiers .srt), ce qui en fait justement des formats appréciés et encore très utilisés. Je pense qu’on est nombreux à encore ouvrir des fichiers textes et s’apercevoir parfois qu’ils sont cassés. On se dit alors que si son éditeur de texte avait de la reconnaissance de codage de caractères, cela aurait aidé !

C’est ainsi que malgré tous ces changements, un logiciel comme uchardet a encore tout son intérêt. Et d’ailleurs de l’autre côté, on voit qu’il y a maintenant plus d’alternatives viables qu’à l’époque.

Tout d’abord, Google a créé CED pour Chrome. Dans un article de blog, un développeur de Mozilla explique pourquoi, selon lui, ce n’était pas une bonne idée de partir sur ce projet, dans leur cas :

License-wise the code is Open Source / Free Software, but it’s impractical to exercise the freedom to make modifications to its substance, which makes it close to an invariant section in practice (again, not as a matter of license). The code doesn’t come with the tools needed to regenerate its generated parts. And even if it did, the input to those tools would probably involve Google-specific data sources. There isn’t any design documentation (that I could find) beyond code comments. Adopting ced would have meant adopting a bunch of C++ code that we wouldn’t be able to meaningfully change.

Pour résumer, cette personne dit que CED n’est pas vraiment libre en pratique puisqu’on n’a pas les outils pour générer de nouveaux modèles, ni les données source. Donc on ne peut vraiment améliorer le code.

C’est en fait un commentaire très intéressant pour moi, car c’est justement l’état dans lequel Mozilla avait laissé son propre code dans ses dépôts, et donc celui dans lequel je l’ai trouvé en 2015 : de nombreux fichiers de code générés et à la logique opaque. C’est vraisemblablement la raison pour laquelle il y eut si peu de contributions avant moi : personne ne savait vraiment comment améliorer le cœur du logiciel et les rares contributions se concentraient sur l’interface ou l’outil en ligne de commande ; pas de nouveau codage pris en charge, ni aucune amélioration de l’efficacité avant 2015… J’ai passé énormément de temps à essayer de comprendre le code et les données générés (sans savoir comment il l’a été et avec quel jeu de données), à le démystifier et surtout à créer des scripts de génération de modèles de langages à partir de données libres et publiques (contenu de Wikipédia). Mes scripts sont inclus dans le dépôt dès le début, et j’inclus même les logs de génération (ce qui permet de pouvoir étudier ces derniers ultérieurement si un modèle devait être problématique). C’est une forme très limitée d’ingénierie inversée, mais j’en suis plutôt fier, car c’est ce qui rend uchardet vraiment puissant (la simplicité de créer de nouveaux modèles). Les scripts de génération de modèles sont livrés et rien n’est opaque, par design. Néanmoins bien que j’ai démystifié une partie (les modèles pour les codages mono-octets), il reste certaines parties de code peu documentées (les codages multi-octets) qui parfois me laissent encore un peu pantois (je commence en fait à les remplacer progressivement plutôt que chercher absolument à les comprendre).

Quoi qu’il en soit, Firefox lui-même est revenu à faire de la détection depuis Firefox 89 (soit juin 2021). Cela n’est pas fait automatiquement mais en allant dans le menu View > Repair Text Encoding, ce qui fait que le lien vers leur propre page d’archive a encore un rendu cassé par défaut et il faut faire une action explicite dans un menu caché (voir aussi ce post d’intention) pour voir la page proprement.
Et c’est ainsi que Mozilla a décidé de ré-écrire un nouveau projet en Rust. Dans leur article, ils citent uchardet (sans le nommer autrement qu’un « fork non-Mozilla » avec un lien vers la page d’uchardet) et le fait qu’il est bien plus complet maintenant qu’il ne l’était à l’époque, mais qu’il manque encore des fonctionnalités. Ils ont donc préféré ré-implémenter du début (une logique qui m’interpelle) et ce nouveau projet s’appelle chardetng.

Notons qu’il existe une troisième alternative, nommée ICU. Je ne l’ai jamais testée (puisque je n’en ai plus l’intérêt, avec maintenant un système qui marche vraiment bien: uchardet! 😜), mais toujours d’après le développeur Firefox, ce détecteur serait bien moins précis que toutes les alternatives (uchardet, chardetng et CED donc) et aurait donc pour cette raison été rejeté par les équipes de Chrome et Mozilla.

Comment fonctionne uchardet?

J’imagine que les projets cités utilisent des logiques similaires, même si je n’ai pas vérifié ces derniers en détail. Concentrons-nous sur uchardet.

Algorithme naïf et premier concept: caractères interdits

Globalement l’idée de base des détecteurs de la vieille époque est qu’il suffit de vérifier si on tombe sur des octets interdits. Cela marche relativement bien pour certains codages multi-octets (tels que UTF-8) puisqu’il est assez facile de créer des données invalides dès qu’on écrit hors ASCII. Ces codages multi-octets sont d’ailleurs souvent assimilables à des machines à états (avec des états "invalides").

Néanmoins c’est bien moins vrai pour les jeux de caractères mono-octets. Énormément parmi ces derniers utilisent quasiment tout l’intervalle donné (voire son entièreté si on considère les caractères de contrôle comme admissibles, voir par exemple ISO-8859-15). En outre, même lorsqu’ils sont incomplets, plusieurs codages occupent souvent les mêmes intervalles.

Ainsi ISO-8859-1 et ISO-8859-15 sont exactement sur les mêmes intervalles. Si on se limite à détecter les caractères interdits, il est ainsi impossible de différencier la validité en se contentant de tester le texte comme étant dans l’un de ces jeux de caractères: toute suite d’octets valide dans une version est valide dans l’autre également !

Voir par exemple les tableaux des 2 codages où on voit que les mêmes intervalles d’octets sont utilisés:

Comparaison de ISO-8859-1 et ISO-8859-15

C’est pourquoi un algorithme qui ne cherche qu’à vérifier la validité de la séquence ne peut dire que "Luke, je suis ton pÚre!" ne convient pas puisque c’est une séquence valide en ISO-8859-15 (mais en tentant de lire un texte UTF-8).

Malheureusement divers logiciels libres utilisaient (et utilisent encore) une telle logique naïve : boucler en passant le texte dans iconv, testant alternativement des jeux de caractères dans une liste. Puis on s’arrête au premier jeu qui ne retourne pas en erreur (ce n’est pas un exemple au hasard, j’ai vu cet algorithme dans certains gros logiciels). Avec une telle logique, vous pouvez être certain de ne quasiment jamais avoir le bon résultat pour la plupart des jeux mono-octets. 😱

Statistiques des caractères utilisés dans un langage

C’est là où le second concept apparaît: « même si les mêmes emplacements sont utilisés dans 2 jeux de caractères, tous les emplacements ne sont possiblement pas tous utilisés dans tous les langages. »

Dans notre exemple fictif, le caractère '€' se trouve à l’emplacement 0xA4 dans ISO-8859-15, et '¤' (« symbole monétaire générique ») dans ISO-8859-1. On peut voir comment cela met ISO-8859-15 en avant pour un texte d’une langue européenne. De même, les caractères 0xBC et 0xBD représentent “Œ” et “œ” respectivement en ISO-8859-15, et '¼' et '½' en ISO-8859-1. Bien qu’on puisse avoir des quarts et demis, on sait que ces derniers sont peu utilisés, alors que dans un texte en français, les e-dans-l’o sont des caractères utilisés.

Par contre, ISO-8859-15 peut aussi être utilisé dans divers autres langages, par exemple l’italien qui n’utilise pas le “œ”. Ainsi trouver ce caractère pourrait augmenter la confiance qu’on soit en train de lire un texte français et diminuer celle qu’on soit en présence d’un texte italien.

Et si on obtenait des statistiques de caractères qui apparaissent par langage ?
On découvre ainsi que certains caractères sont extrêmement courants, quand d’autres sont très rares voire inexistants selon le langage. Les statistiques se révèlent suffisamment différentes même entre langues sœurs.

Séquences de caractères

Néanmoins ces statistiques de caractère ne sont toujours pas assez.
Le troisième concept est donc d’aller regarder non plus par caractère, mais par séquence de caractère. On se rend compte ainsi que si on a un “œuf” ou un "½uf », les 2 étant des résultats valides pour un même ensemble de 3 octets selon qu’on les décode avec la table ISO-8859-15 ou ISO-8859-1, statistiquement, “œ” suivi de “u” est une séquence relativement courante en français (“œuf”, “sœur”, “bœuf”…) mais ce n’est pas le cas de '½' suivi de “u”. Si on avait encore le moindre doute qui pouvait subsister, il n’est probablement plus.

Ce dernier concept est donc de générer des statistiques de suites de 2 caractères. En fait, on se limite à 2 lettres (et non “caractère”, terme plus générique qui inclut aussi des symboles, des chiffres, etc.), ce qui fait que mon exemple n’est pas aussi significatif que voulu — '½' n’étant pas une lettre — mais vous voyez l’idée!

Ce concept sur les séquences de lettres donne des résultats extrêmement pertinents pour qualifier le langage d’un texte.

Conclusion : un mix de tout ça!

Le principe de uchardet et peut-être de tous les autres systèmes de détection modernes est donc d’utiliser ces 3 concepts. La détection naïve est toujours d’actualité, mais elle ne sert que de raccourci pour les cas les plus simples.

À partir de statistiques par langues (générées et compilées dans des modèles), on cherchera donc à identifier le langage le plus probable. En fait, uchardet est autant un détecteur de langage qu’il est un détecteur de codage de caractère. On cherche à calculer les probabilités qu’une suite d’octets soit un texte d’un langage donné avec un codage donné. On assigne ainsi un score “confiance” pour chaque couple (langage, codage). Et on retourne simplement le jeu de caractère du couple avec le plus haut score.

Ces statistiques sont compilées à partir de texte Wikipédia, grâce à mon script qui génère des tables en fichiers source qui seront compilés. C’est pourquoi la détection est au final extrêmement rapide tout en étant précise.

Conclusion 2 : uchardet ne fait pas de magie mais n’en est pas moins impressionnant !

J’ai de temps en temps des rapports de bugs avec des gens qui se plaignent que des textes de test ne sont pas convenablement détectés. Parfois il s’agit soit de tests sur quelques caractères à peine, soit de caractères aléatoires.

Vous l’aurez compris, dans le premier cas, uchardet pourrait fonctionner mais étant un outil statistique, il est évident que plus il y a de texte, plus on peut avoir confiance en la réponse. uchardet ne fait notamment pas de recherche dictionnaire (ce qui aurait un tout autre coût temporel et rendrait l’outil très lent) ni d’étude grammaticale. Les chances de trouver le bon jeu de caractère si tout ce que vous avez est un unique mot de quelques caractères est forcément plus sujet à erreur.

Dans le second cas, c’est carrément contraire à la logique d’uchardet. On a eu des cas où des gens testaient une seule lettre dans divers jeux de caractères et n’étaient pas content qu’uchardet ne trouvait pas le bon jeu. Comme expliqué plus haut, d’autant plus si le caractère est mono-octet, sa version codée est probablement valide dans des dizaines de jeux de caractères, voire tous. Dans le cas d’une séquence aléatoire de caractères, cela ne fera qu’embrouiller les modèles statistiques de la librairie qui cherchera à assigner un score selon des fréquences de séquences qui ne correspondent en fait à aucune langue.

Il y a aussi le fait de mélanger les langues, ce qui n’est pas un problème en soi s’il y a suffisamment de texte (puisqu’on travaille statistiquement, même s’il y a une citation dans une langue étrangère, on peut supposer que le gros du texte est dans la langue à chercher, et c’est suffisant pour faire pencher la balance). Une variante de cela est les balises de formatage de fichier, lesquelles contiennent souvent de l’anglais. J’en parlais plus haut dans mon exemple avec un fichier HTML. Il est conseillé de nettoyer les balises et ne garder que le contenu pour une détection plus fiable. Bien sûr, ces balises ne feront que baisser un peu les scores et si le vrai contenu est suffisamment conséquent, cela pose peu de problème.

Enfin uchardet ne détecte donc pas des codages, mais bien des couples de (langage, codage). La liste précise de la version 0.0.8 est disponible sur le site. C’est ainsi que nous pouvons détecter un texte par exemple en ISO-8859-15 écrit en danois, finnois, français, irlandais, italien, norvégien, portugais, espagnol et suédois. S’il advenait qu’on cherche à tester un texte utilisant ce codage dans un autre langage, il y a de fortes chances que l’outil fonctionnerait moins bien : il pourrait quand même donner le bon codage, mais ce serait par chance (probablement par similarité statistique avec une autre langue, mais sûrement avec un score de concordance médiocre). Une façon de contribuer à uchardet est donc de créer des fichiers de langue, des fichiers de jeux de caractère puis lancer le script de génération de modèle comme expliqué dans ce petit README pour développeurs. Plus nous prendrons en charge de couples de langue et codage, plus l’outil sera utile.

Futur : API de détection de langage

J’ai commencé (depuis 2020, à intervalle irrégulier) de travailler à étendre l’usage d’uchardet pour faire de la détection de langage. Comme je l’ai expliqué, c’est en fait déjà le cas en interne, sauf que la langue détectée ne ressortait pas dans les résultats de l’outil. Historiquement l’outil ne sert que pour détecter le codage d’un fichier.

J’ai toujours trouvé cela dommage et c’est donc la direction que prend le projet puisque je viens de fusionner ma branche de travail de long terme sur le dépôt principal, pour la future version uchardet 0.1.0.
uchardet 0.0.8 est donc probablement la dernière version qui ne retourne que le codage du texte. La version suivante permettra aussi d’en connaître le langage.

Cette future version retournera désormais une liste de candidats et les scores de confiance de chacun, permettant par exemple à un logiciel de ne pas juste se fier au premier résultat. Si 2 résultats ont un score très proche, un logiciel intéractif pourrait décider d’afficher une boîte de dialogue proposant les 2 codages avec prévisualisation, ou en demandant la langue présumée du texte.

J’ai aussi commencé à ajouter des systèmes de poids (pas comme les vieilles bibliothèques de détection bien sûr, au contraire pour permettre d’implémenter des systèmes plus intelligents), mais j’en parlerai dans une future dépêche pour ne pas trop spoiler.

J’ai pas mal de développements en cours, non seulement sur l’API mais aussi sur la précision de détection qui a déjà fait un énorme bond entre la version 0.0.8 à peine sortie et le code en développement.
De manière générale, même si les 3 concepts explicités plus haut sont toujours globalement à la base de la détection, je les ai fait évoluer, avec de nouveaux concepts ou sous-concepts et j’ai revu les calculs. C’était en réalité déjà le cas dans les versions 0.0.*, mais ça le sera encore davantage avec les versions 0.1.* à venir.
Certaines parties du calcul originel des scores de confiance n’avaient en fait pas beaucoup de sens statistique et marchaient parfois un peu par “chance” (disons plutôt : sûrement des tests arbitraires des développeurs d’origine qui avaient des effets de bords valides statistiquement, mais avec une logique un peu bancale). Je revois donc ces calculs en permanence en me basant sur mes expérimentations statistiques. Ça me prend pas mal de temps, car il commence à y avoir beaucoup de modèles à régénérer à chaque changement dans la logique des modèles (plus d’une centaine de modèles de séquences pour plusieurs dizaines de modèles de langues). Les résultats sont en tous cas extrêmement prometteurs.

L’une des conséquences, notamment, est que les versions suivantes devraient prendre en charge beaucoup plus de couples de (langage, codage), et avec plus de fiabilité.

J’ai encore du travail sur cette branche, mais elle avance bien. J’espère pouvoir sortir uchardet 0.1.0 en 2023.

Conclusion et financement

Cette bibliothèque est un petit projet annexe (à mon plus gros projet, GIMP) qui m’a toujours beaucoup plu puisque j’aime énormément le traitement des langues (je maintiens d’ailleurs quelques autres projets autour de ce sujet).
Je le développe par à-coup, notamment lorsque j’ai besoin de faire une petite pause dans mon développement sur GIMP (la charge est lourde), ce qui fut le cas ce mois de décembre, où j’ai fait un mini burn-out après la sortie de GIMP 2.99.14 et avais besoin de décompresser en codant autre chose (ce qui ne m’a pas empêché de faire quelques commits sur GIMP, des revues de code, de gérer les activités au jour le jour et de répondre à des rapports de bug 😅 ; peut-être devrais-je revoir ma définition de pause…).

En tous les cas, j’espère que vous appréciez ce projet qui est déjà pas mal utilisé.

Au passage, je ne peux que rappeler que si vous appréciez mon code, vous pouvez me financer à travers le projet “ZeMarmot” (sur Liberapay ou Patreon notamment, ou même par donations en virement direct en contactant l’association LILA).
Certes uchardet n’est pas un produit direct du projet “ZeMarmot” mais c’est le moyen le plus sûr de me payer officiellement pour développer du code libre, et par conséquent tout autre projet libre que je fais en est aussi une conséquence indirecte (y compris uchardet). Donc si vous appréciez uchardet et l’utilisez dans votre code (ou si vous l’utilisez indirectement parce que vous regardez des films avec mpv ou un lecteur basé sur QtAV), financez “ZeMarmot” pour assurer la pérennité de cette librairie, s’il vous plaît! 🤗

Je vous souhaite de belles fêtes de fin d’année à tous, et ce quelle que soit la langue:

$ echo "Joyeux Noël et bonnes fêtes de fin d’année!" | uchardet -V

    UTF-8 / fr (0.793221)
    UTF-8 / vi (0.706612)
    UTF-8 / sl (0.665267)
    UTF-8 / sv (0.661734)
    UTF-8 / es (0.622741)
    []
$ echo "메리 크리스마스!" | uchardet -V

    UTF-8 / ko (0.990000)
    IBM866 / ru (0.396848)

Note: l’option -V donnant les scores de confiance et les candidats de couples de codage/langue ne se trouve pour l’instant que dans le code en développement, qui a détecté le français et coréen ici. Inutile de le tester sur uchardet 0.0.8. C’est un petit teaser sur le futur d'uchardet! 😜
🎅🎉🥂

Aller plus loin

  • # équivalent à la biblio Python FTFY

    Posté par  (Mastodon) . Évalué à 4. Dernière modification le 21 décembre 2022 à 16:18.

    Ça ressemble à la bibliothèque Python FTFY dont on peut aussi trouver une version en ligne ici :

    https://ftfy.vercel.app/

    • [^] # Re: équivalent à la biblio Python FTFY

      Posté par  (site web personnel, Mastodon) . Évalué à 10. Dernière modification le 21 décembre 2022 à 19:16.

      C'est un concept dérivé, mais c'est pas du tout la même chose:

      Is ftfy an encoding detector?

      No, it’s a mojibake detector (and fixer). That makes its task much easier, because it doesn’t have to guess the encoding of everything: it can leave correct-looking text as it is.

      Encoding detectors have ended up being a bad idea, and they are largely responsible for creating the problems that ftfy has to fix.

      The text that you put into ftfy should be Unicode that you’ve attempted to decode correctly. ftfy doesn’t accept bytes as input.

      There is a lot of Unicode out there that has already been mangled by mojibake, even when decoded properly. That is, you might correctly interpret the text as UTF-8, and what the UTF-8 text really says is a mojibake string like “réflexion” that needs to be decoded again. This is when you need ftfy.

      En gros, ce projet (que je ne connaissais pas) part du principe que l'on ne va pas détecter correctement le codage d'origine (et qu'on travaille forcément en UTF-8 en codage final), et va essayer de corriger votre résultat de conversion erronée. En d'autres termes, on pourrait utiliser FTFY après avoir utilisé uchardet si ce dernier s'est planté.

      Évidemment je ne suis pas du tout d'accord avec leur "lemme" qui est qu'on ne sait pas faire de la détection de codage ("Encoding detectors have ended up being a bad idea"). J'ai mes nombreuses années de travail sur uchardet qui prouvent que ce lemme est faux. uchardet est vraiment vraiment bon. Je ne dis pas que ce projet (ftfy) est totalement inutile. Il peut l'être pour les rares cas où uchardet se plante. Mais leur argument de base est faux: si, de nos jours, on sait globalement très bien et très efficacement détecter le codage de flux de textes donc le codage d'origine est inconnu. Genre vraiment très bien. Essayer uchardet le prouve.

      Ensuite bien sûr qu'on peut encore améliorer l'efficacité, et des gens comme moi y travaillent (comme je le dis, même si je viens à peine de sortir la version 0.0.8, la future version 0.1.0 a déjà fait un bond d'efficacité avec des nouveaux concepts que j'expérimente; ça m'impressionne moi-même parfois). Et surtout il faut ajouter de plus en plus de prises en charge de langues et codages. J'en ai déjà ajouté plusieurs ces derniers jours. Je pense que je pourrai avoir quelques dizaines de modèles supplémentaires pour la prochaine version.

      P.S.: modérateurs de LinuxFr: un éditeur a dû être un peu trop zêlé avec une regexp:

      $ echo « Joyeux Noël et bonnes fêtes de fin d’année! » | uchardet -V
      []
      $ echo "메리 크리스마스! » | uchardet -V

      Ce sont des strings de lignes de commandes shell, donc c'est bien " et non «  ou  » qui doivent être affichés sur les 2 commandes! 😜

      Film d'animation libre en CC by-sa/Art Libre, fait avec GIMP et autre logiciels libres: ZeMarmot [ http://film.zemarmot.net ]

      • [^] # Re: équivalent à la biblio Python FTFY

        Posté par  (site web personnel) . Évalué à 3.

        Corrigé, merci.

      • [^] # Re: équivalent à la biblio Python FTFY

        Posté par  . Évalué à 2.

        Mais leur argument de base est faux: si, de nos jours, on sait globalement très bien et très efficacement détecter le codage de flux de textes donc le codage d'origine est inconnu

        La détection de charset est très utile, mais ça devrait être du dernier recours quand tout à échoué. Ça marche très bien, mais ce n'est pas parfait.

        Qu'est-ce qu'il se passe si je voulais vraiment écrire « ½uf » ? Les outils de détection automatiques risquent d'altérer mon texte. C'est peu probable, mon texte sera probablement plus gros et d'autres éléments permettront de désambiguïser, mais il vaut mieux que le charset soit fourni avec le texte dans le format qui le contient ou dans les métadonnées de transfert, qu'il soit correct et qu'il ne soit pas perdu.

        Aussi, c'est triste que ça ne fasse pas 20 ans qu'on n'ait pas bougé à UTF-8 ou UTF-32 par défaut pour tout ce qui est écrit dans un alphabet latin (voir tout, tout court). Parce que bon, finalement, les problèmes d'encodage, ça vient principalement du fait que certains systèmes s'obstinent à pondre du windows-1252 ou du windows-1256, parfois à le déclarer comme du ISO-8859-15 ou inversement. Avec les fichiers de sous-titres justement, il y en a encore plein avec des soucis d'encodage, même de films et séries récentes. On voit encore passer plein de mails en windows-xxx, pour peu qu'ils aient été produits avec des outils MS. Cette négligence nous coûte de la complexité et les détecteurs de charset ont en effet encore de beaux jours devant eux. Je comprends bien qu'il y a des enjeux de rétrocompatibilité, mais il y a un moment où faut décider de corriger les choses, fournir des outils de compatibilité et arrêter les bêtises.

        Je comprends la frustration : si ces outils n'existaient pas, peut-être que les logiciels qui produisent du contenu s'assureraient peut-être de standardiser sur un sous-ensemble utile de charset ( distinguables entre eux par le fait que les caractères de l'un sont illégaux dans l'autre) au lieu de laisser tout le monde se débrouiller avec des formats tous pétés.

        • [^] # Re: équivalent à la biblio Python FTFY

          Posté par  (site web personnel, Mastodon) . Évalué à 10. Dernière modification le 22 décembre 2022 à 18:31.

          Qu'est-ce qu'il se passe si je voulais vraiment écrire « ½uf » ?

          Si ton bout de texte, c'est juste "½uf", c'est clair que c'est pas assez et uchardet ne peut pas te sortir un résultat fiable (je vais même pas prendre la peine de tester; si c'est pour tester sur 3 octets, vous vous êtes trompé de logiciel; même si ça trouvait le bon résultat, j'attribuerais cela à la chance). Mais si ton texte est "Bonjour monsieur, pourriez-vous me servir 2 douzaines d'½ufs, une bouteille de lait, et une baguette s'il vous plaît", tu auras bien du mal à me convaincre (et "convaincre" uchardet) que c'est un décodage pertinent (en comparaison à: "Bonjour monsieur, pourriez-vous me servir 2 douzaines d'œufs, une bouteille de lait, et une baguette s'il vous plaît" puisque le concept est de comparer les scores, je le rappelle, comme techniquement le premier décodage est aussi valide, mais le texte lui ne l'est pas en français).

          Le test en réel avec uchardet 0.0.8:

          echo "Bonjour monsieur, pourriez-vous me servir 2 douzaines d'œufs, une bouteille de lait, et une baguette s'il vous plaît" | iconv -f UTF-8 -t ISO-8859-15 | uchardet 
          ISO-8859-15
          

          L'erreur dont tu parles se produit si uchardet se plante et retourne ISO-8859-1 (auquel cas, en recodant en UTF-8, tu obtiens ½ à la place de œ). Comme tu le vois, sur un vrai texte complet, uchardet ne se plante pas.

          Il peut y avoir des cas particuliers où tu voudrais vraiment dire "½uf", par exemple je pourrais imaginer un texte plus grand qui traite justement du sujet des codages de caractères et donne un exemple de décodage loupé. Mais dans ce cas, le texte est vraisemblablement bien plus long, du moins suffisamment pour justement faire pencher la balance vers le bon codage quand même (celui qui gardera "½uf" sur l'endroit où c'est ce que tu voulais et "œuf" sur les endroits où c'est ce que tu veux). De toutes façons, ce texte plus long, totalement hypothétique, ne sera ni en ISO-8859-15, ni ISO-8859-1 puisqu'aucun des 2 n'a les 2 caractères (donc tu ne peux traiter ce sujet dans un tel codage). Ce texte serait probablement en UTF-8.

          En gros, tu essaies de raisonner en terme de "comment arriver à tromper le système". Et oui, soyons clair, tu peux toujours tromper un système informatique, trouver des cas bizarres et extrêmes, surtout si le système est basé sur les statistiques. Il suffit par exemple de travailler dur pour produire un vrai texte en français en faisant exprès d'aller à l'encontre de toutes les statistiques habituelles de la langue française, en utilisant des tournures particulières, le maximum de mots peu usités, du franglais ou le plus de mots étrangers possibles (mais qu'on utilise quand même en France), etc. Oui tu pourras toujours me sortir un exemple qui pête un tel système. Parce que c'est pas un système fait pour déjouer les ruses, ce qui n'a aucun intérêt selon moi. C'est un système fait pour être utilisé dans la vie réelle, sur de vrais textes de diverses langues. Et dans ce contexte, uchardet fonctionnera 99,99% des fois sur les codages et langues prises en charge (statistiques tirées du chapeau mais franchement j'attends encore les contre-exemples de la vraie vie et pas créés spécifiquement pour ruser et tromper l'outil). 😇

          Pour moi, ton d'exemple (et "si je voulais vraiment écrire « ½uf »", je sais pas combien de fois dans votre vie, vous avez voulu écrire ce mot "½uf" si courant 🙄 dans notre langue, le mettre dans un fichier ou une base de donnée, sans aucun autre texte contextuel, juste ce mot et c'est tout, puis vous êtes demandés plus tard son codage! Moi perso: jamais! 😜) est à ranger dans la catégorie de ceux qui donne des bouts de 3 lettres aléatoires et s'étonnent qu'uchardet ne trouve pas le codage qu'ils avaient en tête: sauf qu'uchardet ne fait toujours pas de magie ni ne lit dans la tête des gens! 😛

          La détection de charset est très utile, mais ça devrait être du dernier recours quand tout à échoué. Ça marche très bien, mais ce n'est pas parfait.
          […]
          Cette négligence nous coûte de la complexité et les détecteurs de charset ont en effet encore de beaux jours devant eux. Je comprends bien qu'il y a des enjeux de rétrocompatibilité, mais il y a un moment où faut décider de corriger les choses, fournir des outils de compatibilité et arrêter les bêtises.

          Tout ce que tu dis, c'est comme dire que c'est nul qu'on soit pas encore tous passé à IPv6. Donc faut arrêter de faire en sorte que son logiciel fonctionne avec IPv4 parce que c'est le passé (soyez libre de ne pas prendre en charge IPv4 dans un logiciel réseau ahahah!) ou que Wayland est le futur, donc faut arrêtez de faire en sorte que nos logiciels marche avec X11 (on entend ça parfois; mais va dire aux développeurs de logiciel d'abandonner la majeure partie des utilisateurs sous Linux!), etc.

          Dans les faits, nous on veut juste que nos machines ou logiciels marchent. C'est tout. C'est pas être en désaccord que la nouvelle technologie X est peut-être vraiment meilleure ou pas que l'ancienne technologie Y. C'est juste que les problèmes d'Y existent encore et qu'on peut pas faire comme s'ils n'existaient pas. Typiquement pour en revenir à mon problème de sous-titres, j'allais pas dire "oooh comment il ose ce gus qui a fait gratuitement des sous-titres pour les donner à tous, et qui a même pas utilisé UTF-8 pour cela! Quel goujât! Pour la peine, je vais pas utiliser ses sous-titres!". Non je dis: "zut, j'ai vraiment envie de voir ce film mais on a besoin des sous-titres. Ce serait bien si mon lecteur vidéo était capable de détecter le bon codage sans que je m'en mêle!" Et donc je règle ce problème (je me suis arrangé pour que mon lecteur détecte au mieux le codage). Parce que je peux pas retrouver tous les gens sur internet qui font des sous-titres en hobby (et même si je le pouvais, aller leur faire la leçon sur le codage de caractère — un truc qu'ils ne comprendront sûrement pas de toutes façons parce qu'ils s'en fichent — n'est pas un truc que j'ai particulièrement envie de faire).
          La théorie est belle, mais bon… Perso je pense aussi que la détection de charset a encore de très beaux jours devant elle (comme la détection de langage, qui est le nouvel usage d'uchardet; j'en parlerai plus en détail à la prochaine sortie du logiciel) mais j'y mets aucun sentiment.

          Je comprends la frustration

          Ahahah. Je suis pas du tout frustré par la situation. J'ai plus l'impression que c'est toi qui est frustré sur le coup, non? 😉

          si ces outils n'existaient pas, peut-être que les logiciels qui produisent du contenu s'assureraient peut-être de standardiser sur un sous-ensemble utile de charset

          LOL Y a bien une trentaine d'années (probablement plus) d'histoire de l'informatique pour réduire à néant ta théorie. 😆

          Comme l'historique dans mon article le montre, ce type de logiciel de détection de codage a vraiment commencé à être utilisé vers 2015 (pour uchardet qui commence à être utilisé dans quelques logiciels à partir de 2015; puis le développement de CED dont le premier commit est de 2016; puis chardetng dont la première sortie est 2019…). La réalité, c'est juste que c'est encore très peu utilisé globalement et que donc les gens ont toujours constamment des problèmes de codage dans une majorité de logiciels. Donc dire que c'est la faute de ce type d'outil qu'on est dans cette situation, c'est complètement inverser les responsabilités et le cause à effet.

          La réalité est juste: (1) on est dans cette situation, c'est comme ça et c'est un fait; (2) ça s'améliore progressivement mais ça prend du temps et en attendant, c'est quand même mieux si on arrive à lire les fichiers sans problèmes de décodage bizarre et (3) même si un jour, tout le monde utilise UTF-8, ça n'efface pas magiquement le passé pour autant, ainsi que les milliards de fichiers et textes en base de données qui ont été créés depuis des décennies et dont une grosse partie va encore être utilisés quelques décennies de plus.

          Donc on est dans cette situation, c'est comme ça. Autant tirer le meilleur parti d'une situation bordélique plutôt qu'énoncer des "et si". 😁

          Film d'animation libre en CC by-sa/Art Libre, fait avec GIMP et autre logiciels libres: ZeMarmot [ http://film.zemarmot.net ]

          • [^] # Re: équivalent à la biblio Python FTFY

            Posté par  . Évalué à 3. Dernière modification le 22 décembre 2022 à 21:05.

            tu auras bien du mal à me convaincre (et "convaincre" uchardet) que c'est un décodage pertinent

            Mais je ne devrais avoir personne à convaincre de quoi que ce soit : si je saisis un texte, il devrait ressortir tel quel. Je m'en fiche de savoir que tu penses que le contenu ne soit pas pertinent. Mon argument, c'est que la détection de charset, c'est bien, si on ne peut pas faire autrement (et évidemment que dans le monde imparfait dans lequel on vit, c'est souvent le cas - je m'en servirai volontiers en cas de besoin.)

            Ahahah. Je suis pas du tout frustré par la situation

            Je parlais des gens qui on fait ftfy.

            Par ailleurs je suis assez d'accord avec tout ce que tu dis. Et ce que fait uchardet et les outils similaires, c'est ultra utile dans le contexte du monde pas idéal dans lequel on est. C'est super que des gens comme toi bossent dessus. Évidemment. Faut pas prendre mon message comme une attaque. Si on est dans une situation cassée, ok, on va utiliser ce qu'on peut pour retrouver le contenu original vraisemblable avec des stats et des heuristiques. Mais c'est important de reconnaitre et documenter les limitations, même si on ne peut rien faire de mieux.

            Juste que ça devrait être du dernier recours. À chaque fois qu'on en a besoin, c'est qu'il y a un bug quelque part (mauvais passage de l'information du charset utilisé).

          • [^] # Re: équivalent à la biblio Python FTFY

            Posté par  . Évalué à 0.

            ce que tu dis, c'est comme dire que c'est nul qu'on soit pas encore tous passé à IPv6.

            Non, parce que le standard omniprésent, c'est déjà l'UTF-8 (pas comme IP où le standard omniprésent, c'est encore IPv4)…

            Mais c'est vrai que c'est con qu'on dépende encore d'IPv6, moi ça me coûte 1€ par mois pour avoir une adresse IPv4 sur mon serveur… :-)

            Donc faut arrêter de faire en sorte que son logiciel fonctionne avec IPv4 parce que c'est le passé

            Non, mon souhait c'est qu'un truc qui produit du texte le fasse en utf-8 ou utf-32. Celui qui le lit peut continuer à comprendre ISO-machin-truc ou windows-chose-bidule. Mais oui, je crois vraiment que les mécanismes de détection automatique de charset risquent de baisser les motivation de réparer le problème que ça résout à la source. Devrait-on alors arrêter de faire de la détection automatique de charset ? Non, ne ne pense pas non plus.

            ou que Wayland est le futur, donc faut arrêtez de faire en sorte que nos logiciels marche avec X11

            Bof, j'utilise toujours X11, de moins en moins convaincu par Wayland xD
            Mais bon, peut-être que ça va bientôt marcher…

  • # Commande file

    Posté par  (site web personnel) . Évalué à 4.

    En général, j'utilise la commande file -i qui me donne le langage, le type de caractère de fin de ligne et l'encodage. À vrai dire, je ne sais pas sur quel algorithme elle se base…

    • [^] # Re: Commande file

      Posté par  (site web personnel, Mastodon) . Évalué à 10.

      Mais file connaît quasi rien de la plupart des encodages, ou alors t'as une version spéciale! Tiens, je le lance sur un jeu de fichiers de test du dépôt d'uchardet:

      $ file test/et/*
      test/et/iso-8859-13.txt:  ISO-8859 text
      test/et/iso-8859-15.txt:  ISO-8859 text
      test/et/iso-8859-4.txt:   ISO-8859 text
      test/et/utf-8.txt:        UTF-8 Unicode text
      test/et/windows-1252.txt: Non-ISO extended-ASCII text
      test/et/windows-1257.txt: ISO-8859 text

      Il donne "ISO-8859 text" pour une majorité, ce qui veut pas dire grand chose (je connais au moins 15 IS0-8859-* codages et hormis la partie ASCII, ils sont tous vraiment vraiment différents (c'est même le but). En plus, il se plante dans un cas (WINDOWS-1257 aussi catégorisé en "ISO-8859 text"), et il sait pas dans un autre (WINDOWS-1252 qu'il dit juste être "Non-ISO", ce qui aide pas trop!).

      Dernier point: on n'est pas du tout sur les mêmes ordres de grandeur en terme de rapidité:

      $ time uchardet test/et/*
      test/et/iso-8859-13.txt: ISO-8859-13
      test/et/iso-8859-15.txt: ISO-8859-15
      test/et/iso-8859-4.txt: ISO-8859-4
      test/et/utf-8.txt: UTF-8
      test/et/windows-1252.txt: WINDOWS-1252
      test/et/windows-1257.txt: WINDOWS-1257
      
      real    0m0.008s
      user    0m0.004s
      sys 0m0.004s
      $ time file test/et/*
      test/et/iso-8859-13.txt:  ISO-8859 text
      test/et/iso-8859-15.txt:  ISO-8859 text
      test/et/iso-8859-4.txt:   ISO-8859 text
      test/et/utf-8.txt:        UTF-8 Unicode text
      test/et/windows-1252.txt: Non-ISO extended-ASCII text
      test/et/windows-1257.txt: ISO-8859 text
      
      real    0m0.044s
      user    0m0.036s
      sys 0m0.008s

      Parce qu'un outil comme file, en plus d'être faux ou imprécis, se traîne comme un escargot alors qu'uchardet est fait pour être rapide, juste et précis. 😜

      Film d'animation libre en CC by-sa/Art Libre, fait avec GIMP et autre logiciels libres: ZeMarmot [ http://film.zemarmot.net ]

      • [^] # Re: Commande file

        Posté par  (site web personnel, Mastodon) . Évalué à 10. Dernière modification le 22 décembre 2022 à 03:35.

        Ah en relisant ton commentaire, je découvre que tu mentionnes en fait une option -i particulière que je ne connaissais pas. Dans ce cas, je vois ça essaie en effet de donner un vrai nom de charset complet, mais c'est vraiment pas glorieux:

        $ file -i test/et/*
        test/et/iso-8859-13.txt:  text/plain; charset=iso-8859-1
        test/et/iso-8859-15.txt:  text/plain; charset=iso-8859-1
        test/et/iso-8859-4.txt:   text/plain; charset=iso-8859-1
        test/et/utf-8.txt:        text/plain; charset=utf-8
        test/et/windows-1252.txt: text/plain; charset=unknown-8bit
        test/et/windows-1257.txt: text/plain; charset=iso-8859-1

        À part UTF-8, pas une seule bonne réponse! (le nom du fichier est la réponse, comme on se l'imagine)

        Film d'animation libre en CC by-sa/Art Libre, fait avec GIMP et autre logiciels libres: ZeMarmot [ http://film.zemarmot.net ]

        • [^] # Re: Commande file

          Posté par  (site web personnel) . Évalué à 3.

          Ma question complémentaire est donc : est-il possible d'intégrer ucharset dans file ? C'est un peu le principe d'UNIX depuis le début d'améliorer les outils si les algo évoluent.

          • [^] # Re: Commande file

            Posté par  (site web personnel, Mastodon) . Évalué à 3.

            file fait bien ce pour quoi il est prévu

            NAME
                 file – determine file type
            

            Et le principe d'UNIX depuis le début est d'avoir le bon outil pour régler un problème donné, et ici il ne s'agit pas des mêmes problèmes : file type ≠ file encoding

            “It is seldom that liberty of any kind is lost all at once.” ― David Hume

            • [^] # Re: Commande file

              Posté par  . Évalué à 4. Dernière modification le 31 décembre 2022 à 08:24.

              Pourtant il tente de le faire. Si une fonction marche aussi mal généralement soit on la retire soit on tente de la corriger, pourquoi refuser ? La dépendance peut tout à fait être optionnelle voir passer par une interface pour accepter d'autres bibliothèques ou éviter des problèmes de licences.

              https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

              • [^] # Re: Commande file

                Posté par  (site web personnel, Mastodon) . Évalué à 1. Dernière modification le 01 janvier 2023 à 15:34.

                Non, ils ne tentent pas de le faire (donner l'encodage) ; c'est une incompréhension : ça dit juste que c'est du multi-octets (là l'algo naif de combinaisons discriminantes et le fait qu'il y ait peu de choix fait que ça tomber juste) ou du 7 bits (ascii) ou du 8 bits (dit ascii étendu)
                édition : je viens de voir que du 8 bits c'est toujours iso-8859-1 sauf quelques exceptions déjà invoquées ; donc ils tentent pas vraiment de te dire si ton texte est du turc ou du vietnamien.

                Oui, une solution pourrait être la dépendance optionnelle : si uchardet est présent alors lui faire appel pour avoir un truc précis sinon on passe dans la routine maison qui envoie un truc à la grosse louche.

                “It is seldom that liberty of any kind is lost all at once.” ― David Hume

                • [^] # Re: Commande file

                  Posté par  . Évalué à 2.

                  Non, ils ne tentent pas de le faire (donner l'encodage) ; c'est une incompréhension : ça dit juste que c'est du multi-octets (là l'algo naif de combinaisons discriminantes et le fait qu'il y ait peu de choix fait que ça tomber juste) ou du 7 bits (ascii) ou du 8 bits (dit ascii étendu)

                  S'il voulait dire que c'est du multi-octets il indiquerait multi-octets. Là il indique ascii, iso-8859-1 ou utf8 qui sont des encodages.

                  https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

          • [^] # Re: Commande file

            Posté par  (site web personnel, Mastodon) . Évalué à 6.

            C'est possible techniquement et sans aucun doute très facile à faire (en plus file est écrit en C). En outre file m'a l'air d'être encore développé/maintenu. Ensuite je me dis que c'est le genre de projet où ils veulent sûrement limiter l'usage de dépendances au maximum (de ce que je vois dans leur build, il n'y a que des dépendances à des bibliothèques de compression), donc ce serait possiblement le point bloquant. Mais ça ne coûte rien de demander. Le suivi de bug/demande de fonctionnalité se fait là apparemment: https://bugs.astron.com/view_all_bug_page.php

            Et puis je suis d'accord que si ce logiciel souhaite prétendre savoir faire cela, autant le faire bien (ou pas du tout). D'ailleurs je viens de jeter un œil au code de détection de charset (dans src/encoding.c), et comme je m'y attendais, c'est une logique totalement naïve:

             * A file is considered to be ISO-8859 text if its characters are all
             * either ASCII, according to the above definition, or printing characters
             * from the ISO-8859 8-bit extension, characters 0xA0 ... 0xFF.
             *
             * Finally, a file is considered to be international text from some other
             * character code if its characters are all either ISO-8859 (according to
             * the above definition) or characters in the range 0x80 ... 0x9F, which
             * ISO-8859 considers to be control characters but the IBM PC and Macintosh
             * consider to be printing characters.

            D'ailleurs de ce que j'entrevois du code, je crois que tout texte "ISO-8859" retourne "iso-8859-1" avec l'option -i qui n'est donc en fait pas plus précise du tout. Le code ne m'a l'air de faire aucune différence entre les charsets de cette famille (ou d'autres famille de charsets mono-octets) et l'option -i semble juste produire un format de sortie différent mais pas vraiment faire plus de traitement.

            Enfin bon, oui, s'ils veulent vraiment prétendre à donner un charset précis pour des fichiers sans métadonnées, ce programme aurait tout intérêt à utiliser uchardet, au moins de manière optionnelle. Je suis d'accord. N'hésitez pas à le leur proposer en ouvrant un rapport de bug.

            Film d'animation libre en CC by-sa/Art Libre, fait avec GIMP et autre logiciels libres: ZeMarmot [ http://film.zemarmot.net ]

            • [^] # Re: Commande file

              Posté par  (site web personnel) . Évalué à 3.

              Merci de ta réponse.

              Il est possible aussi que la commande file ne lise que le 1er bloc afin d'avoir une réponse très rapide. De mémoire, elle utilise la libmagic qui, lorsque c'est possible, ne regarde que les premiers octets. Je ne sais pas si uchardet s'auto limite sur la taille du fichier.

      • [^] # Re: Commande file

        Posté par  (site web personnel, Mastodon) . Évalué à 1.

        Cela ne me surprend guère parce-que le but de file est de détecter le MIME-type en gros. Les réponses « ISO-8859 text » indiquent juste que c'est bien un fichier pure texte mais avec un encodage un octet… Et là, comme dit la dépêche, en testant juste les combinaisons interdites, on ne sait pas différencier les différents cas ISO-8859 entre eux…
        Ton commentaire suivant ne fait que confirmer cet état de fait. L'option i tente de discriminer la forme de texte probable (par exemple « texte/html » mais est rarement mieux pour discriminer les ISO-8859-NN ou les windows-NNNN etc.) Bref, le commentaire initial a une mauvaise compréhension de la problématique et de ce que faite la commande qu'il évoque.

        “It is seldom that liberty of any kind is lost all at once.” ― David Hume

  • # Mémoire collective VS réalité

    Posté par  . Évalué à 2.

    Je me permets ce petit HS:

    La phrase " Luke, je suis ton père! ", pourtant iconique, n'apparait dans aucun film Starwars (et donc pas dans l'épisode 5, Vador ne prononce pas (exactement) cette phrase).

    Vous ne me croyez pas, allez vérifier !

    • [^] # Re: Les faits cachés derrière la réalité

      Posté par  . Évalué à 4. Dernière modification le 22 décembre 2022 à 11:05.

      La phrase existait mais a été supprimée du scénario par les producteurs comme pouvant donner une image trop négative de la famille aux jeunes États-Uniens. ;-)

  • # Réseau de neurones

    Posté par  . Évalué à 3. Dernière modification le 22 décembre 2022 à 10:57.

    Cette approche statistique m'a tout de suite fait pensé à une solution à base de réseau de neurones. A-t-il déjà été envisagé d'explorer une telle solution pour détecter langue et encodage de caractère. J'imagine qu'avec un jeu de données qui contient des milliers d'articles Wikipédia, et ce dans plusieurs centaines de langues, ça devrait permettre d'obtenir de bons résultats. L'avantage que je vois c'est que le réseau analyserait également les mots et la grammaire d'une langue en plus des points que tu as indiqué. L'apprentissage pourrait être gourmand en ressource mais une fois le réseau entraîné, la vitesse d'exécution du programme ne devrait pas être long.

  • # Double décodage malencontreux

    Posté par  (site web personnel) . Évalué à 5.

    Il y a plus de 25 ans, j'avais fait des petits programme en C qui avaient pour but de transformer des textes HP compatibles avec les consoles HP2626, les PC de l'époque, le Roman8 et l'utf8 sans oublier le codage des caractères html.

    Je me suis déjà trouvé devant des textes qui avaient été recodés deux fois. Le retour en arrière est très difficile. C'est le cas de man en français qui comporte des articles convertis et d'autres pas. En ignorant cela, on a appliqué la même conversion à tous les textes et on se retrouve avec la moitié des textes illisibles.

    Comment repérer un codage double ?
    Peut-on automatiquement repérer les fichiers mal recodés et les réparer ?

    • [^] # Re: Double décodage malencontreux

      Posté par  (site web personnel, Mastodon) . Évalué à 6.

      Le but d'un projet comme uchardet, c'est d'éviter que ce genre de trucs se produisent (ce qui est en effet assez courant et justement pourquoi un projet tel qu'uchardet est nécessaire).

      Quand tu es déjà devant un fait accompli (ré-encodage cassé), j'ai l'impression que c'est plutôt le genre de projet dont quelqu'un parle plus haut (FTFY) dont tu as besoin. Note que j'ai jamais testé et n'ai aucune idée de son efficacité. Mais ce dont tu parles est exactement le cas d'usage qu'ils décrivent dans leur README.

      Film d'animation libre en CC by-sa/Art Libre, fait avec GIMP et autre logiciels libres: ZeMarmot [ http://film.zemarmot.net ]

  • # Ah, c'est grâce à toi…

    Posté par  . Évalué à 6.

    … que depuis un bon moment je n'ai plus à aller sans arrêt dans la config des s-t de smplayer pour basculer entre encodage latin-1 et utf-8, suivant les s-t que je chargeais avec le film ? (en fait, maintenant que tu en parles, c'est peut-être venu tout simplement au moment où j'ai mis mpv comme player au lieu de mplayer, va savoir, après tant d'années).

    Sois-en remercié jusqu'à la neuvième bobine du dernier film qui passera dans mon smplayer !
    Ainsi que pour ta dépêche, fort intéressante.

    J'avais pensé à un truc bien plus simple, du style « encodage de la config, mais utf-8 si le player détectait un unicode », mais ça n'aurait pas marché dans le sens contraire. Je n'imaginais pas en tout cas un processus aussi complexe derrière tout ça.

    • [^] # Re: Ah, c'est grâce à toi…

      Posté par  (site web personnel, Mastodon) . Évalué à 7.

      Sois-en remercié jusqu'à la neuvième bobine du dernier film qui passera dans mon smplayer !
      Ainsi que pour ta dépêche, fort intéressante.

      De rien. 🤗

      J'avais pensé à un truc bien plus simple, du style « encodage de la config, mais utf-8 si le player détectait un unicode »

      En fait c'est ce que la plupart des logiciels faisaient (et que beaucoup font encore).

      Le premier point (codage de la config par défaut) fonctionne dans un monde où on s'échange jamais de fichiers entre personnes (et donc on crée nos propres fichiers et on ne travaille jamais que sur ceux-ci). Mais la réalité est toute autre: en vrai, on s'échange constamment des fichiers (et cette tendance a été décuplée avec internet). Et même le cas du "on crée nos propres fichiers" reste imparfait. Les machines et OS ont évolué, les codages par défaut aussi. Je dois avouer ne plus bien me souvenir des locales par défaut dans nos Linux, y a 20 ans, mais ça ne m'étonnerait pas plus que cela, si à l'époque, on avait des charset non-UTF-8 configurés par défaut dans nos systèmes en fonction de la langue qu'on configurait dans les préférences systèmes. Cela voudrait donc dire que si on essaie de relire nos vieux fichiers de l'époque (ceux sans métadonnées de charset), on aurait des problèmes de décodage.

      D'ailleurs je viens de faire une recherche vite fait sur le web et trouve encore des documentations, genre du Linux Documentation Project, qui conseille aux francophones d'utiliser ISO-8859-15: https://tldp.org/HOWTO/Francophones-HOWTO-6.html

      Donc même dans le cas hypothétique où on n'échangerait jamais de fichiers avec autrui, on pourrait se retrouver avec des décodages problématiques, juste parce qu'on met à jour son OS ou parce qu'on a plusieurs machines.

      Pour le point sur la détection de UTF-8 (ou autre codage multi-octet), comme je dis dans l'article, cela reste relativement simple puisqu'il est très simple de tomber sur des cas invalides. En gros, on peut croire que se limiter au premier concept (détection de caractère invalide, autrement dit l'algorithme naïf) est suffisamment fiable au moins pour ce qui est de la détection de UTF-8. Néanmoins même cela n'est pas parfait. Contrairement à la détection des charsets mono-octets, j'ai encore un peu trop de faux positifs/négatifs à mon goût, liés à une mauvaise détection de UTF-8. C'est à dire, qu'avec uchardet 0.0.8, il m'arrive encore parfois que uchardet sorte UTF-8 par erreur avec un score légèrement meilleur que la bonne réponse (un codage mono-octet), comme il arrive à l'inverse qu'un codage mono-octet sorte en réponse erronée avec un score qui bat de très peu le score du codage UTF-8. Dans les 2 cas, la raison est parce que cette version d'uchardet a gardé la logique que pour UTF-8, la vérification naïve suffit (cette croyance vient de l'algorithme originel, de l'époque où c'était un code Mozilla). Mais ce n'est pas vrai et le score attribué avec cette étape seulement n'est simplement pas assez subtil, donc pas fiable.

      Et ce sera une des grandes différences (parmi d'autres) avec uchardet 0.1.0: même pour UTF-8, il y aura maintenant de la détection statistique par langage. Cela rend malheureusement uchardet moins bon pour les langages non pris en charge, mais en même temps, pour tous les langages pris en charge, cela a tout simplement rendu l'outil d'autant plus fiable et efficace. Franchement c'est sans équivoque, avec mes récentes améliorations, je n'ai simplement plus de ce genre de faux positifs/négatifs dont je parlais plus haut.

      Enfin bon, je suis déjà très fier des améliorations qu'on trouvera dans la version suivante. Je pense même qu'on va pouvoir améliorer plusieurs trucs pour rendre les résultats encore plus fiables (mais faut que je fasse plus de tests). 😊

      Film d'animation libre en CC by-sa/Art Libre, fait avec GIMP et autre logiciels libres: ZeMarmot [ http://film.zemarmot.net ]

  • # Belle dépêche

    Posté par  . Évalué à 5.

    Super, merci pour cette dépêche et ce travail remarquable. Tu multiplies les contributions utiles décidément.

    J'ai longtemps eu du mal à ne pas avoir l'impression que "chardet" est une erreur de frappe sur "charset", avec le S qui est juste à côté du D en qwerty / azerty. Bref.

    Et c’est ainsi que Mozilla a décidé de ré-écrire un nouveau projet en Rust. Dans leur article, ils citent uchardet (sans le nommer autrement qu’un « fork non-Mozilla » avec un lien vers la page d’uchardet)

    Ça vaut peut-être le coup de leur signaler. C'est une formulation maladroite et malheureuse, et je ne pense pas que ça soit malicieux.

    et le fait qu’il est bien plus complet maintenant qu’il ne l’était à l’époque, mais qu’il manque encore des fonctionnalités. Ils ont donc préféré ré-implémenter du début (une logique qui m’interpelle)

    En tout cas je vois un intérêt de travailler en Rust. S'il y a bien une brique logicielle qu'il est intéressant d'avoir en Rust dans un navigateur web, c'est une détection de caractère, avec probablement plein de fichiers d'entrée tout pétés avec des failles de sécurités plus facilement évitables dans ce langage.

    Notons qu’idéalement, vous devriez même retirer tout texte structurel qui pourrait interférer avec la détection (j’en reparlerai dans la section sur les principes de détection. Par exemple pour retirer les tags HTML sur une seule ligne:

    $ curl https://www-archive.mozilla.org/projects/intl/universalcharsetdetection | sed 's/<[^>]*>//g' | uchardet
    

    (il manque une parenthèse fermante dans le texte)

    Ça nécessite que sed lui-même se débrouille suffisamment bien pour détecter le charset xD. Heureusement, en général, les caractères spéciaux XML (< et >) sont toujours codés de la même manière, compatible avec l'UTF-8 que sed attend probablement. Sauf en UTF-16, UTF-32, UCS-2 ou des trucs comme ça, auquel cas sed ne va juste rien transformer, et uchardet sera probablement capable de reconnaître le bon charset correctement. Donc si ça se trouve, ça fonctionne tout le temps, mais pour des raisons un peu plus intéressante que ce qu'on pourrait penser initialement.

    Et du coup, la détection de langage dont tu parles, c'est des langages naturels comme le français, des langages informatiques comme l'HTML, ou les deux ?

  • # quid de recode ?

    Posté par  (site web personnel) . Évalué à 5.

    La dépêche ne mentionné pas recode. Personnellement, pour changer de jeux de caractères j'utilise / j'ai utilisé les deux. Par pure curiosité y a-t-il une raison à sa non mention ?

    • [^] # Re: quid de recode ?

      Posté par  (site web personnel, Mastodon) . Évalué à 7.

      Je connais pas recode, alors je viens de l'installer et je suis en train de regarder son man mais j'ai pas l'impression du tout qu'il fasse de la détection de charset, juste de la conversion.

      D'ailleurs même en cherchant sur le web, je vois que c'est une problématique de savoir quel est le charset de base, puisque les gens cherchaient comment n'utiliser cet outil que conditionnellement en fonction du charset (bon le lien est une vieille question, donc ils essaient divers ruses un peu bancales; de nos jours, on conseillerait un outil tel que uchardet). Donc j'ai pas l'impression que recode fasse de détection du tout, si?

      Et donc si confirmé, cet outil est plutôt dans la catégorie d'iconv que d'uchardet. Ce qui fait que même si je l'avais connu, je l'aurais sûrement pas cité. Ce qui devrait répondre doublement à ta question ([1] je connaissais pas et [2] il fait quelque chose d'autre).

      Ceci dit, merci de la découverte, cet outil me paraît très intéressant. Déjà j'aime beaucoup la façon dont il liste les charsets: recode -l liste par lignes d'alias, alors que iconv -l liste tout en bordel. La question de savoir si un nom est l'alias d'un autre ou une variante légèrement différente (ou complètement différent même) est une grosse question que je me suis souvent posée (dont la quête de réponse obligeait jusque là à faire des recherches laborieuses) et j'envisageais même de faire un script pour répondre à ce type de question. Bon recode répond pas entièrement à toutes les questions (seulement les alias), mais c'est déjà très bien.

      J'aime aussi beaucoup cette option --find-subsets car la question de savoir quel charset est un sous-ensemble de quel autre charset est aussi une grosse problématique. C'est vraiment super.

      Bon par contre, maintenant ça m'apporte potentiellement de nouveaux problèmes, du genre pour les noms de charsets légèrement différents, et savoir quoi correspond à quoi d'un outil à l'autre. J'avais déjà eu la problématique posée en m'apercevant qu'il y a en fait 2 implémentations d'iconv (GNU libc et GNU libiconv) qui peuvent avoir des noms de charset légèrement différents (on a eu au moins un cas rapporté parce qu'il s'avère que sur macOS, ils ont la version libiconv installée qui utilise MACCENTRALEUROPE pour un charset appelé MAC-CENTRALEUROPE dans glibc!). Du boulot en plus quoi!
      Pour ça, je te remercie pas, monsieur! (non, j'rigole! 😁)

      Enfin bon, j'envisageais déjà de probablement ajouter une API pour permettre de choisir le format de sortie (je voulais faire ça pour glibc vs libiconv; je suppose que je pourrais rajouter recode maintenant).

      Film d'animation libre en CC by-sa/Art Libre, fait avec GIMP et autre logiciels libres: ZeMarmot [ http://film.zemarmot.net ]

      • [^] # Re: quid de recode ?

        Posté par  (site web personnel) . Évalué à 3.

        Oui recode existe depuis très longtemps. Il a l'avantage d'être plus simple à utiliser pour les trucs simple qu'iconv.

        Et pour les trucs super simple : unix2dos et dos2unix ;-)

  • # Le secret d'Utrac am le rouge

    Posté par  . Évalué à 2.

    « Ils ont donc préféré ré-implémenter du début (une logique qui m’interpelle) »

    Je me joins à ton interpellation pour te présenter le projet Utrac, qui n'est pourtant plus à présenter, ayant déjà fait l'objet d'un journal ici (et surtout parce qu'il n'est plus du tout maintenu alors que complètement bug'é) et qui semble faire la même chose de la même manière :
    https://linuxfr.org/users/calandoa/journaux/reconnaissance-dencodage-avec-utrac

    Les liens ayant expiré depuis longtemps voici le nouvel ancien lien du projet pour les archéologues amateurs :
    https://utrac.sourceforge.net

    « utrac: - universalchardet ne t’as jamais dit ce qui était arrivé à ton père ?
    uchardet : - Oh, il m’en a dit assez. Il a dit que vous l’avez tué !
    utrac: - Non. Je SUIS ton père.
    uchardet : - Noooooooonnnnnn !!! C'est impossible ! »

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.