Tout d’abord, je voudrais dire que j’ai cherché de manière exhaustive cette solution. Il est important que j’utilise sed
ou au moins un mélange d’utilitaires de ligne de commande * nix pour résoudre ce problème. Je m’occupe, dans certains cas, de fichiers CSV mal formés, mais je suis presque sûr que cela peut être résolu. Il me manque une seule pièce du puzzle.
Je voudrais construire un convertisseur de CSV à pipe. Cela devrait résoudre les problèmes suivants:
","
et remplacer par |
",
et remplacer par |
,"
et remplacer par |
dog,"john "bud" smith",cat
(devient dog|john bud smith|cat
) ,
qui ne sont pas entre guillemets et remplacer par |
J’ai terminé presque tout cela avec une commande sed
, mais je suis coincé avec les virgules qui sont dans un champ. Il y a probablement une meilleure façon, mais je suis à court de pensée créative sur le sujet. Une solution appropriée parsingra cette chaîne:
1234,"bill","butler","1000,p"r"airie",1234,6789
dans
1234|bill|butler|1000,prairie|1234|6789
C’est ce que j’ai jusqu’à présent:
echo '1234,"bill","butler","1000,p"r"airie",1234,6789' | sed -e 's/","/|/g' -e 's/,"/|/g' -e 's/",/|/g' -e 's/"//g'
Vous pouvez utiliser perl
. Text::Parsewords
à la rescousse:
perl -MText::ParseWords -nle 'print join "|", map {s/"//g; $_} parse_line(",",1,$_);' file
Pour votre échantillon, il produirait:
1234|bill|butler|1000,prairie|1234|6789
echo '1234,"bill","butler","1000,p"r"airie",1234,6789' | sed -e 's/\([0-9"]\),\([0-9"]\)/\1|\2/g' -e 's/"//g'
J’ai défini une règle:
, is transformed to the | if it is between numbers or quotes
et plus tard, effacez toutes les citations
EDIT1 On dirait que ma solution ne fonctionne pas, mais il y a un bon fil pour cette question
#!/bin/bash l='1234,"bill","butler","1000,p"r"airie",1234,6789' has_quote_in_quote() { echo $1 | grep -q '[^,]"[^,]' } clean_quote_in_quote () { echo $1 | sed -E -e 's/([^,])"([^,])/\1\2/g' } parse() { echo $1 |grep -E -o '[^"]*|"[^"]*"' } pipe_unquoted_commas() { for f in $(parse $1); do echo $f|sed -E -e '/^[^"]/s/,/|/g'; done } while has_quote_in_quote $l; do b=$(clean_quote_in_quote $l); l=$b; done echo $(printf "%s" $(pipe_unquoted_commas $b|sed 's/"//g'))
En cours d’exécution cela donne
1234|bill|butler|1000,prairie|1234|6789
Ce n’est pas évident pour moi que c’est ce que vous voulez, mais laissez-moi vous expliquer comment cela fonctionne.
has_quote_in_quote trouve n’importe quel ” ‘qui n’est pas voisin d’une virgule. clean_quote_in_quote supprime tous ceux qu’il peut trouver, mais s’ils sont vraiment proches, il a besoin de plus d’un passage parce que sed a dépassé le seul caractère cité – donc que ce soit par hasard ou délibérément, votre exemple était vraiment bien choisi. Parse choisit un texte non cité ou cité, y compris les guillemets. Les guillemets sont supprimés dans la boucle while et les virgules sont transformées dans le dernier ligne, tandis que les caractères de citation restants sont supprimés.
// P