J'ai un certain nombre de fichiers CSV volumineux et je les souhaite en TSV (format séparé par des tabulations). La complication est qu'il y a des virgules dans les champs du fichier CSV, par exemple:
A,,C,"D,E,F","G",I,"K,L,M",Z
Production attendue:
A C D,E,F G I K,L,M Z
(où les espaces entre les deux sont des onglets "durs")
J'ai Perl, Python et coreutils installés sur ce serveur.
Ajouter au fichier nommé csv2tab.sh
, et le rendre exécutable
#!/usr/bin/env python
import csv, sys
csv.writer(sys.stdout, dialect='Excel-tab').writerows(csv.reader(sys.stdin))
$ echo 'A,,C,"D,E,F","G",I,"K,L,M",Z' | ./csv2tab.sh
A C D,E,F G I K,L,M Z
$ ./csv2tab.sh < data.csv > data.tsv && head data.tsv
1A C D,E,F G I K,L,M Z
2A C D,E,F G I K,L,M Z
3A C D,E,F G I K,L,M Z
Utilisation de csvkit
(Python), par exemple:
$ csvformat -T in.csv > out.txt
Est-ce que le streaming, avec des citations et échappements CSV et TSV corrects
C'est dans apt et autres gestionnaires de paquets
Pour le plaisir, sed
.
sed -E 's/("([^"]*)")?,/\2\t/g' file
Si votre sed
ne prend pas en charge -E
, essayez avec -r
. Si votre sed
ne prend pas en charge \t
pour un onglet littéral, essayez de mettre un onglet littéral (dans de nombreux shells, ctrl-vtab) ou dans Bash, utilisez un $'...'
Chaîne de style C (auquel cas la barre oblique inverse dans \2
doit être doublé). Si vous souhaitez conserver les guillemets, utilisez \1
au lieu de \2
(auquel cas la paire de parenthèses internes est inutile et peut être supprimée).
Cela n'essaie pas de gérer les guillemets doubles échappés à l'intérieur des guillemets doubles; certains dialectes CSV le supportent en doublant le double guillemet cité (sic).
Une option pourrait être le module Perl's Text :: CSV par exemple.
Perl -MText::CSV -lne 'BEGIN { $csv = Text::CSV->new() }
print join "\t", $csv->fields() if $csv->parse($_)
' somefile
démontrer
echo 'A,,C,"D,E,F","G",I,"K,L,M",Z' |
Perl -MText::CSV -lne 'BEGIN { $csv = Text::CSV->new() }
print join "\t", $csv->fields() if $csv->parse($_)
'
A C D,E,F G I K,L,M Z
Perl -lne '
my $re = qr/,(?=(?:[^"]*"[^"]*")*(?![^"]*"))/;
print join "\t", map { s/(?<!\\)"//gr =~ s/\\"/"/gr } split $re;
'
awk -v Q=\" -v FPAT="([^,]*)|(\"[^\"]+\")" -v OFS="\t" '{
for (i=1; i<=NF; ++i)
if ( substr($i, 1, 1) == Q )
$i = substr($i, 2, length($i) - 2)
print $1, $2, $3, $4, $5, $6, $7, $8
}'
Résultat:
A C D,E,F G I K,L,M Z
Si vous avez, ou pouvez installer, l'utilitaire csvtool
:
csvtool -t COMMA -u TAB cat in.csv > out.ctv
Notez que pour une raison quelconque, csvtool
n'a pas de page de manuel, mais csvtool --help
imprimera quelques centaines de lignes de documentation.
La solution thermonucléaire de tapette à mouches doit utiliser libreoffice. Alors que https://ask.libreoffice.org/en/question/19042/is-is-possible-to-convert-comma-separated-value-csv-to-tab-separated-value-tsv-via -headless-mode / suggère que ce n'est pas possible mais que c'est faux (ou juste obsolète?) et la commande suivante fonctionne sur mon 5.3 .:
loffice "-env:UserInstallation=file:///tmp/LibO_Conversion" --convert-to csv:"Text - txt - csv (StarCalc)":9,34,UTF8 --headless --outdir some/path --infilter='csv:44,34,UTF8' *.csv
l'argument env
peut être ignoré mais de cette façon, les documents n'apparaîtront pas dans votre document récent.
Utiliser mlr
est presque succinct, mais la désactivation des en-têtes nécessite de longues options:
mlr --c2t --implicit-csv-header --headerless-csv-output cat file.csv
Production:
A C D,E,F G I K,L,M Z
J'ai créé un convertisseur CSV vers TSV open source qui gère les transformations décrites. C'est assez rapide, peut valoir le coup d'œil s'il y a un besoin continu de convertir de gros fichiers CSV. L'outil fait partie de boîte à outils d'utilitaires TSV d'eBay (documentation csv2tsv ici ). Les options par défaut suffisent pour l'entrée décrite:
$ csv2tsv file.csv > file.tsv
Une considération lors de la conversion de CSV en TSV est la gestion des délimiteurs de champ et d'enregistrement (virgule et nouvelle ligne) dans les données. CSV utilise une syntaxe d'échappement. Si l'objectif est d'utiliser la sortie avec des outils Unix comme cut
, awk
, etc., la sortie doit être exempte d'échappements. La plupart des solutions répertoriées ici produisent des échappements de style CSV lorsque des délimiteurs se trouvent dans les données. csv2tsv
se différencie des autres solutions en ce qu'il produit TSV sans échappements. Consultez la documentation pour plus de détails.
Pour voir ce que fait une solution particulière, convertissez un CSV contenant des virgules, des tabulations, des guillemets et des retours à la ligne dans les données. Par exemple:
$ echo $'Line,Field1,Field2\n1,"Comma: |,|","Quote: |""|"\n"2","TAB: |\t|","Newline: |\n|"' | <conversion-script-or-command>
Les solutions générant des échappements mettront des guillemets doubles dans les champs contenant des guillemets, des nouvelles lignes ou des tabulations.
Juste pour le plaisir, les substitutions d'expression régulière peuvent être effectuées dans Vim . Voici une solution potentielle à quatre lignes, adaptée de: https://stackoverflow.com/questions/33332871/remove-all-commas-between-quotes-with-a-vim-regex
Les guillemets sont supprimés.
:%s/".\{-}"/\=substitute(submatch(0), ',', '_' , 'g')/g
:%s/,/\t/g
:%s/_/,/g
:%s/"//g
Pour écrire un peu la solution, les quatre lignes ci-dessus (sans signe deux-points) peuvent être enregistrées dans un fichier, par ex. to_tsv.vim
. Ouvrez chaque CSV pour le modifier avec Vim et source
the to_tsv.vim
script sur la ligne de commande Vim (adapté de https://stackoverflow.com/questions/3374179/run-vim-script -from-vim-commandline/8806874 # 8806874 ):
:source /path/to/vim/filename/to_tsv.vim
Avec Perl
, en supposant que les champs csv n'ont pas de "
Ni de nouvelles lignes ou d'onglets intégrés:
Perl -pe 's{"(.*?)"|,}{$1//"\t"}ge'
Voici l'exemple de conversion de CSV en TSV à l'aide de l'utilitaire jq
:
$ jq -rn '@tsv "\(["A","","C","D,E,F","G","I","K,L,M","Z"])"'
A C D,E,F G I K,L,M Z
ou:
$ echo '["A","","C","D,E,F","G","I","K,L,M","Z"]' | jq -r @tsv
A C D,E,F G I K,L,M Z
Cependant, le format CSV doit être bien formaté, donc chaque chaîne doit être citée.
Source: format de sortie TSV simple .
Ce qui suit est simplement une correction de la réponse de @ tripleee afin qu'il supprime toutes les citations du champ final comme il fait à tous les autres domaines.
Pour montrer ce qui est corrigé, voici la réponse d'un tripleee , plus une légère modification des données d'exemple de l'OP avec des guillemets ajoutés autour de la finale ' [~ # ~] z [~ # ~] 'champ.
echo 'A,,C,"D,E,F","G",I,"K,L,M","Z"' | sed -r -e 's/("([^"]*)")?,/\2\t/g'
A C D,E,F G I K,L,M "Z"
Vous pouvez voir que ' [~ # ~] z [~ # ~] ' est laissé avec des guillemets autour. Ceci est différent de la façon dont les champs internes sont traités. Par exemple, le ' [~ # ~] g [~ # ~] ' n'a pas de guillemets dessus.
La commande suivante utilise une deuxième substitution pour nettoyer la dernière colonne:
echo 'A,,C,"D,E,F","G",I,"K,L,M","Z"' | sed -r -e 's/("([^"]*)")?,/\2\t/g' \
-e 's/\t"([^"]*)"$/\t\1/'
A C D,E,F G I K,L,M Z