Trucs et astuces CLI: Comparer des fichiers CSV, json, geojson

Pour opendatarchives, j’essaye de limiter le volume de données historisé et archivé. Les dates de mise à jour figurant dans les métadonnées sont souvent modifiées alors que les données n’ont pas changé, j’essaye donc de trouver un moyen simple, rapide, pour détecter par exemple que le contenu d’un CSV n’a pas changé.

Fichiers CSV

Un simple calcul de hash pourrait aller sauf que les plateformes qui génèrent ces fichiers à la volée (OpenDataSoft par exemple) ne fournissent pas forcément les lignes du CSV dans le même ordre.
Le hash est différent alors que les lignes sont identiques mais dans un ordre différent.

Un tri des lignes du fichier, avant calcul du hash contourne le problème:

[ "$(sort fichier1 | md5sum)" = "$(sort fichier2 | md5sum)" ] && echo "identique" || echo "different"

Ceci ne fonctionne pas forcément sur des CSV contenant du texte multiligne… dans ce cas il faut utiliser un vrai tri de fichier CSV, par exemple avec csvsort provenant de csvkit:

[ "$(csvsort fichier1 | md5sum)" = "$(csvsort fichier2 | md5sum)" ] && echo "identique" || echo "different"

Son inconvénient apparaît avec de gros fichiers CSV… il consomme de la RAM et du CPU pour le parsing du CSV.

On peut aussi détecter si un fichier CSV « grossit », c’est à dire que des lignes sont ajoutées mais aucune supprimées, avec un sort + diff + grep + wc par exemple pour retourner le nombre de lignes supprimées:

diff <(sort fichier1) <(sort fichier2) | grep '^<' -c

Fichiers json (et geojson)

Là c’est jq qui permet de trier le json avec l’option -S pour le comparer:

[ "$(jq . -S fichier1 | md5sum)" = "$(jq . -S fichier2 | md5sum)" ]  && echo "identique" || echo "different"
3 J'aimes

Pour les geojson, on peut améliorer la comparaison en comparant les « features » triées…

jq .features[] -c -S lefichier | sort |md5sum

jq va extraire chaque élément de l’array « features », la sortir sur une seule ligne (-c = compact), dont le contenu sera trié (-S pour le sort json)
Ensuite, on trie ces lignes représentant chacune un objet géographique pour qu’elles soient dans le même ordre et on calcule le hash global.

1 J'aime

Merci Christian ! Il y a quelques semaines, j’ai eu à comparer différentes versions de l’export CSV de la base Open Food Facts (plus de 2Go / 900 000+ lignes).

La commande diff était dans les choux sur ma petite machine (8 Go de RAM tout de même), y compris en utilisant le paramètre --speed-large-files.

De mémoire, l’usage de rdiff avait en revanche donné de très bon résultats, notamment en suivant cet article explicatif.

Je n’ai pas rencontré ce problème, vu que j’ai nettement plus de RAM… de plus je suis pas encore descendu au niveau « diff », même pour détecter les fichiers qui ne font que grossir, mais ça va arriver :wink: