Un petit billet rapide, sans aucune prétention. Juste pour revenir sur des trucs bizarres sur l’encodage… J’avais commencé à m’y intéresser il y a 3 ans, alors que je discutais avec des utilisateurs de R en Inde et au Japon. Mais revenons à la base… Si on regarde la page “unicode” de Wikipedia (ou plus précisément, si on regarde le code source, on peut avoir des informations (dans les métadonnées) : en l’occurrence, on voit que la page est encodée en UTF-8
C’est cohérent car de plus en plus de documents en ligne semblent encodé dans ce format. Si on revient à la source, l’encodage c’est lié au passage par les bytes 0/1. Plein de sites expliquent (en gros) la philosophie. Quand j’étais petit, on nous expliquait les caractères ASCII, qui utilisent 7 bits ce qui permet de disposer 128 caractères uniquement, numérotés de 0 à 127. Genre “A” était le 65ème caractère, et “a” le 97ème. Il y avait des symboles exotiques (comme “@” qui est le 64ème) ou la ponctuation, mais pas d’accents, comme “à”. Si on veut les lettres avec des accents, il faut plus que 128 caractères. Est-alors arrivée ISO/CEI 8859, qui proposait d’encoder les caractères sur 8 bits (et pas 7). On parle aussi de ISO 8859-1 plus populaire sous le nom “latin-1” très populaires dans les pays latins, avec le “à“, et puis on a continué d’enrichir pour arriver au ISO 8859-15 (latin-9) avec dedans le symbole “€”. Pour les langues asiatiques, il a fallu enrichir encore. Bref, c’est un jeu sans fin. Dans les années 90, est arrivée la norme ISO 10646 correspondant au “jeu universel de caractères” ou UCS pour Universal Character Set (en anglais), qui donnera Unicode. Je crois que les puristes font la différence entre les jeux de caractères (l’ensemble de caractères auxquels on attribue à chacun un point de code unique, la liste des caractères ASCII par exemple) et l’encodage (qui est la façon de représenter ce point de code en mémoire). Mais on va faire simple…
Là où ça se complique c’est que les OS utilisaient (par défaut) des encodages différents. Sous Windows, UTF-16 et Unicode sont majoritairement utilisé. Sous Mac OS,il y avait un truc appelé MacRoman mais il semble que UTF-8 l’emporte aujourd’hui. Enfin, sous linux, historiquement c’était du latin-1 par défaut, mais progressivement l’UTF-8 semble prendre le dessus. Bref, tous ceux qui naviguent entre les OS le savent : les accents c’est l’enfer. Et le principal soucis c’est qu’il est impossible, quand on récupère un fichier d’avoir son encodage a priori. Regardons le fichier suivant BaseANSI.txt
On peut tenter de regarder s’il y a des sortes de méta-données dans le document…
mais ce n’est pas le cas. Le document est un document brut, avec juste les données, et aucune métadonnées. Rien sur l’encodage (en particulier, car c’est ce qui nous intéresse aujourd’hui). Pour l’anecdote, si au lieu d’ouvrir le fichier avec mon bloc-note sous Windows, on tente sur un mac, on a
Et quand je passe par le terminal DOS sous Windows, c’est la même chose (ce qui est un peu rassurant)
Regardons un autre fichier BaseUNICODE.txt. Si vous voyez une différence avec l’autre, dites le moi…
Pourtant il y en a une : la preuve, dans un terminal, on peut lire les accents du fichier,
Un dernier ? Regardons BaseUTF8.txt,Là encore, on a l’impression d’avoir le même fichier. Et pourtant non.
Commençons par récupérer les trois fichiers (via R)
download.file(url="http://freakonometrics.free.fr/BaseUTF8.txt", destfile="BaseUTF8.txt") download.file(url="http://freakonometrics.free.fr/BaseANSI.txt", destfile="BaseANSI.txt") download.file(url="http://freakonometrics.free.fr/BaseUNICODE.txt", destfile="BaseUNICODE.txt") |
Techniquement, ces documents sont différents, même si dans le bloc note, on ne voyait rien : il n’ont pas la même taille !
file.size("BaseUNICODE.txt") [1] 322 file.size("BaseANSI.txt") [1] 163 file.size("BaseUTF8.txt") [1] 171 |
On peut essayer de les lire, pour voir…
read.table("BaseANSI.txt",header=TRUE,sep=";") Département No Habitants 1 Ain 01 631877 2 Ardèche 07 324209 3 Bouches-du-Rhône 13 2016622 4 Corse-du-Sud 2A 152730 5 Côte-dOr;21;533147\nCôtes-dArmor 22 598357 |
On a un petit soucis avec l’apostrophe, mais rien de grave
read.table("BaseANSI.txt",header=TRUE,sep=";",quote="\"") Département No Habitants 1 Ain 01 631877 2 Ardèche 07 324209 3 Bouches-du-Rhône 13 2016622 4 Corse-du-Sud 2A 152730 5 Côte-d'Or 21 533147 6 Côtes-d'Armor 22 598357 |
R arrive parfaitement a lire (sur mon ordinateur du moins) cette première base de données. Tentons une seconde
read.table("BaseUTF8.txt",header=TRUE,sep=";",quote="\"") ï..DÃ.partement No Habitants 1 Ain 01 631877 2 Ardèche 07 324209 3 Bouches-du-Rhône 13 2016622 4 Corse-du-Sud 2A 152730 5 Côte-d'Or 21 533147 6 Côtes-d'Armor 22 598357 |
On a manifestement un soucis, un soucis d’encodage. Le soucis c’est que, d’ordinaire, on ne sait pas comment le fichier a été encodé. Et comme on l’a vu, il n’y a pas de métadonnées qui nous donne l’encodage. Comment faire ? Heureusement, il existe une fonction sous R qui devine l’encodage. Littéralement.
library(readr) guess_encoding("BaseUTF8.txt", n_max = 1000) # A tibble: 3 x 2 encoding confidence 1 UTF-8 1.00 2 ISO-8859-1 0.620 3 ISO-8859-2 0.430 |
Autrement dit, le best-guess est ici un encodage UTF-8. Essayons pour voir,
read.table("BaseUTF8.txt",header=TRUE,sep=";",encoding="UTF-8",quote="\"") X.U.FEFF.Département No Habitants 1 Ain 01 631877 2 Ardèche 07 324209 3 Bouches-du-Rhône 13 2016622 4 Corse-du-Sud 2A 152730 5 Côte-d'Or 21 533147 6 Côtes-d'Armor 22 598357 |
Ça marche ! ou disons que ça marche presque. J’ai un soucis dans mes libellés de variables. Mais disons que c’est anecdotique ici.
Tentons le dernier fichier
read.table("BaseUNICODE.txt",header=TRUE,sep=";",quote="\"") ÿþD 1 NA 2 NA 3 NA 4 NA 5 NA 6 NA 7 NA 8 NA |
Comme ça ne marche pas, essayons d’avoir un best-guess sur l’encodage
guess_encoding("BaseUNICODE.txt", n_max = 1000) # A tibble: 3 x 2 encoding confidence 1 UTF-16LE 1.00 2 ISO-8859-1 0.530 3 ISO-8859-2 0.270 |
On en tient un !
read.table("BaseUNICODE.txt",header=TRUE,encoding="UTF-16LE") ÿþD 1 \n 2 B 3 \n 4 C 5 \n 6 C 7 \n 8 C |
Damned, ça ne marche pas… Heureusement, Ewen est venu à ma rescousse. La solution est relativement étrange
read.table("BaseUNICODE.txt",header=TRUE,fileEncoding="UTF-16LE", sep=";", quote="") Département No Habitants 1 Ain 01 631877 2 Ardèche 07 324209 3 Bouches-du-Rhône 13 2016622 4 Corse-du-Sud 2A 152730 5 Côte-d'Or 21 533147 6 Côtes-d'Armor 22 598357 |
Oui, on n’utilise pas l’encodage via “encoding” mais “fileEncoding” ! Subtil non… Cela dit, si on voulait être cohérent on devrait utiliser la fonction du package pour lire la base, non ? Car dans le package “readr” il y a une fonction “read_table”
read_table("BaseUNICODE.txt",locale=locale(encoding = "UTF-16LE")) Error in guess_types_(datasource, tokenizer, locale, n = guess_max) : Incomplete multibyte sequence |
Sauf que quand on la lance, on a un message d’erreur… Bref, l’encodage c’est compliqué. C’est ce qui est expliqué dans le forum de discussion du package cela dit. Je ne suis pas sûr que mon billet serve à quoi que ce soit, mais je voulais garder une trace de tout ça !