Article original : A quick tale about FEFF, an invisible UTF-8 character that wrecked our CSV files

Par Eumir Gaspar

Aujourd'hui, nous avons rencontré une erreur en essayant de créer des seeds de base de données à partir d'un CSV. Ce CSV avait été généré à l'origine par moi-même en utilisant un script Ruby qui a redirigé la sortie vers un fichier et l'a sauvegardé en tant que CSV.

Le CSV a été vérifié dans Git et avait été utilisé pendant un certain temps jusqu'à ce que nous devions mettre à jour certaines parties en ajoutant une nouvelle colonne et en corrigeant certaines valeurs.

Bien que nous ne connaissions pas encore la raison exacte, ma théorie est que, d'une manière ou d'une autre, Excel pour Mac (nous utilisons tous des Macs) a ajouté des métadonnées supplémentaires même après avoir sauvegardé le fichier en tant que CSV.

Cela a fait que toute personne utilisant le seed recevait l'erreur suivante :

CSV::MalformedCSVError: Illegal quoting in line 1.

J'ai ouvert le fichier CSV et rien ne semblait suspect. Ma première pensée était que des guillemets gauche/droite avaient été mélangés dans le fichier au lieu des guillemets doubles "normaux" : ". Mais après une investigation plus approfondie, il n'y avait rien d'extraordinaire. Cela m'a conduit à effacer tout le fichier et à retaper la première ligne.

J'ai sauvegardé ce fichier à nouveau et exécuté la migration :

CSV::MalformedCSVError: Illegal quoting in line 1.

Quoi ?!

D'accord, cela me rendait fou. J'ai ouvert un nouveau fichier, tapé la même ligne exacte à nouveau, et exécuté la migration. Cela a fonctionné. Alors, qu'y avait-il dans ce fichier ?!

Une seule façon de le découvrir :

cat companies.csv | pbcopy | pbpaste > temp.csv
rm companies.csv
mv temp.csv companies.csv
git diff

Donc, OSX a ces deux fonctions qui sont très utiles : pbcopy et pbpaste. Basiquement, tout ce qui est redirigé vers pbcopy se retrouve dans votre presse-papiers et pbpaste place ce que vous avez dans votre presse-papiers vers la sortie standard (stdout). Mais cela supprime tout le formatage.

Très utile lorsque vous voulez simplement copier du texte de quelque part et le coller dans un éditeur WYSIWYG sans tout le formatage. Comme lorsque vous écrivez un email depuis Gmail, par exemple.

J'ai ensuite supprimé le fichier original et sauvegardé le nouveau fichier "non formaté" avec le même nom de fichier afin de voir la différence.

Et nous avons enfin vu l'homme invisible :

Image Le caractère invisible s'affichant dans Bitbucket d'Atlassian.

Image Le nom réel du caractère invisible !

Une rapide recherche Google nous a appris que notre ami U+FEFF s'appelait un ZERO WIDTH NO-BREAK SPACE. De plus, un rapide tour sur Wikipedia nous a informés sur les utilisations réelles de U+FEFF, plus communément connu sous le nom de Byte order mark ou BOM.

Notre ami FEFF signifie différentes choses, mais c'est essentiellement un signal pour un programme sur la façon de lire le texte. Il peut s'agir de UTF-8 (plus courant), UTF-16, ou même UTF-32.

FEFF lui-même est pour UTF-16 — en UTF-8, il est plus communément connu sous le nom de 0xEF,0xBB, ou 0xBF.

D'après ma compréhension, lorsque le fichier CSV a été ouvert dans Excel et sauvegardé, Excel a créé un espace pour notre passager clandestin invisible, U+FEFF. Et devant le fichier en plus !

Excel a fait de la magie, et il a probablement été sauvegardé en UTF-16 au lieu de UTF-8. UTF-8 ne comprend pas BOM et le traite simplement comme un non-caractère, donc visuellement, le fichier était correct. Mais le CSV de Ruby pensait qu'il y avait quelque chose de mal parce qu'il supposait que le fichier qu'il lisait était en UTF-8 et il ne pouvait pas ignorer M. U+FEFF.

Donc, leçon apprise : n'ouvrez pas (et ne sauvegardez pas !) un fichier CSV dans Excel si vous voulez le donner à l'analyseur CSV de Ruby.

Si vous rencontrez une erreur de ce type, assurez-vous de rechercher des caractères cachés non affichés par votre éditeur. Si vous ne pouvez toujours pas le voir et utilisez OSX, alors pbcopy et pbpaste vous aideront — ils suppriment tout formatage ou caractères cachés du texte en plus de le copier et de le coller.