J'ai un fichier d'entrée délimité par des virgules ( ,
). Certains champs entourés de guillemets doubles contiennent une virgule. Voici l'exemple de ligne
123,"ABC, DEV 23",345,534.202,NAME
J'ai besoin de supprimer toutes les virgules qui se produisent à l'intérieur des guillemets doubles et des guillemets doubles également. Donc, la ligne ci-dessus doit être analysée comme indiqué ci-dessous
123,ABC DEV 23,345,534.202,NAME
J'ai essayé ce qui suit en utilisant sed
mais sans donner les résultats attendus.
sed -e 's/\(".*\),\(".*\)/\1 \2/g'
Des astuces rapides avec sed
, awk
ou tout autre utilitaire Unix s'il vous plaît?
Réponses:
Si les citations sont équilibrées, vous souhaiterez supprimer les virgules entre toutes les autres citations, cela peut être exprimé
awk
comme ceci:Sortie:
Explication
Le
-F"
fait que awk sépare la ligne au niveau des signes de guillemet double, ce qui signifie que tous les autres champs seront le texte entre guillemets. La boucle for s'exécutegsub
, abréviation de globalement substitute, sur tous les autres champs, en remplaçant virgule (","
) par Nothing (""
). La1
fin invoque le code-bloc par défaut:{ print $0 }
.la source
gsub
et expliquer brièvement comment fonctionne cette doublure? S'il vous plaît.{ print $0 }
. J'ai également ajouté cela à l'explication.prefix,"something,otherthing[newline]something , else[newline]3rdline,and,things",suffix
(c'est-à-dire: plusieurs lignes et imbriquées "," n'importe où dans un guillemet double multiligne: la"...."
partie entière doit être rejointe et l'intérieur,
doit être remplacé / supprimé ...): votre script ne verra pas de paires de guillemets doubles dans ce cas, et ce n'est pas vraiment facile à résoudre (besoin de "rejoindre" les lignes qui sont dans un "ouvert" (c'est-à-dire, un nombre impair) double citation ... + faites attention s'il y a aussi un échappé\"
à l' intérieur de la chaîne)awk -F'"' -v OFS='"' '{ for (I=1; i<=NF; i+=2) gsub(",", "|", $i) } 1' infile
Il y a une bonne réponse, en utilisant sed une seule fois avec une boucle :
Explication:
:a;
est une étiquette pour une branche plus éloignées/^\(\([^"]*,\?\|"[^",]*",\?\)*"[^",]*\),/\1 /
pourrait contenir 3 pièces fermées[^"]*,\?\|"[^",]*",\?
correspond à une chaîne ne contenant pas de guillemet double, peut-être suivie d'un coma ou d' une chaîne entourée de deux guillemets doubles, sans coma et peut-être suivie d'un coma.ta
sera mis en boucle:a
si las/
commande précédente a changé.la source
Une solution générale qui peut également gérer plusieurs virgules entre guillemets équilibrés nécessite une substitution imbriquée. J'implémente une solution en perl, qui traite chaque ligne d'une entrée donnée et ne substitue que des virgules dans toutes les autres paires de guillemets:
ou en bref
Vous pouvez soit diriger le texte que vous souhaitez traiter vers la commande, soit spécifier le fichier texte à traiter comme dernier argument de ligne de commande.
la source
[^\\]
va avoir l'effet indésirable de faire correspondre le dernier caractère à l'intérieur des guillemets et de le supprimer (non \ caractère), c'est-à-dire que vous ne devez pas consommer ce caractère. Essayez(?<!\\)
plutôt.[^"]*
pour rendre le non-gourmand match (c. -à- tout correspond d'une"
à la suivante"
):perl -pe 's/"([^"]+)"/($match = $1) =~ (s:,::g);$match;/ge;'
. Il ne reconnaît pas l'idée bizarre qu'une citation puisse être échappée avec une barre oblique inverse :-)[^"]*
approche ou l' approche non gourmande explicite consomme moins de temps processeur.J'utiliserais une langue avec un analyseur CSV approprié. Par exemple:
la source
Vos deuxièmes citations sont déplacées:
De plus, l'utilisation d'expressions régulières a tendance à correspondre à la partie la plus longue possible du texte, ce qui signifie que cela ne fonctionnera pas si vous avez plusieurs champs entre guillemets dans la chaîne.
Un moyen qui gère plusieurs champs entre guillemets dans sed
C'est également un moyen de résoudre ce problème, cependant, avec une entrée pouvant contenir plus d'une virgule par champ cité, la première expression dans le sed devra être répétée autant de fois que le contenu maximal de la virgule dans un seul champ, ou jusqu'à ce qu'elle ne change pas du tout la sortie.
L'exécution de sed avec plusieurs expressions doit être plus efficace que plusieurs processus sed exécutés et un "tr" fonctionnant tous avec des tuyaux ouverts.
Cependant, cela peut avoir des conséquences indésirables si l'entrée n'est pas correctement formatée. c'est-à-dire des guillemets imbriqués, des guillemets non terminés.
En utilisant l'exemple en cours d'exécution:
Sortie:
la source
sed -r ':r; s/("[^",]+),([^",]*)/\1 \2/g; tr; s/"//g'
.En perl - vous pouvez utiliser
Text::CSV
pour analyser cela, et le faire trivialement:Vous pouvez imprimer avec
Text::CSV
mais cela a tendance à conserver les citations si vous le faites. (Bien que, je suggère - plutôt que de supprimer les guillemets pour votre sortie, vous pouvez simplement analyser en utilisantText::CSV
en premier lieu).la source
J'ai créé une fonction pour boucler à travers chaque caractère de la chaîne.
Si le caractère est une citation, la vérification (b_in_qt) est marquée comme vraie.
Alors que b_in_qt est vrai, toutes les virgules sont remplacées par un espace.
b_in_qt est défini sur false lorsque la prochaine virgule est trouvée.
la source