web-dev-qa-db-fra.com

Supprimer la virgule entre les citations uniquement dans un fichier délimité par des virgules

J'ai un fichier d'entrée délimité avec des virgules (,). Certains champs sont conçus dans des citations doubles qui ont une virgule dans elles. Voici l'exemple de ligne

123,"ABC, DEV 23",345,534.202,NAME

J'ai besoin de supprimer toutes les virgules se produisant à l'intérieur des citations doubles et des citations doubles. De sorte que la ligne ci-dessus devrait être analysée comme indiqué ci-dessous

123,ABC DEV 23,345,534.202,NAME

J'ai essayé les suivants en utilisant sed mais ne pas donner des résultats escomptés.

sed -e 's/\(".*\),\(".*\)/\1 \2/g'

Des astuces rapides avec sed, awk ou tout autre utilitaire UNIX s'il vous plaît?

23
mtk

Si les devis sont équilibrés, vous voudrez supprimer des virgules entre tous les autres devis, cela peut être exprimé en awk comme ceci:

awk -F'"' -v OFS='' '{ for (i=2; i<=NF; i+=2) gsub(",", "", $i) } 1' infile

Sortir:

123,ABC DEV 23,345,534.202,NAME

explication

Les -F" Donne l'AWK séparez la ligne aux signes à double citation, ce qui signifie que tous les autres champs seront le texte inter-devis. La boucle fonctionne gsub, court pour substitut globalement, sur tous les autres champs, remplaçant la virgule (",") avec rien (""). Les 1 À la fin invoque le bloc de code par défaut: { print $0 }.

32
Thor

Il y a une réponse bonne, en utilisant SED simplement une fois avec une boucle :

echo '123,"ABC, DEV 23",345,534,"some more, comma-separated, words",202,NAME'|
  sed ':a;s/^\(\([^"]*,\?\|"[^",]*",\?\)*"[^",]*\),/\1 /;ta'
123,"ABC  DEV 23",345,534,"some more  comma-separated  words",202,NAME

Explication:

  • :a; Est une étiquette de la branche furtrice
  • s/^\(\([^"]*,\?\|"[^",]*",\?\)*"[^",]*\),/\1 / pourrait contenir 3 parties fermées
    • d'abord le 2e: [^"]*,\?\|"[^",]*",\? Match pour une chaîne ne contenant pas de double citation, peut-être suivie d'un coma o une chaîne jointe à deux doubles citation, sans coma et peut-être suivis d'un coma.
    • que le premier REAR est composé de comme beaucoup de répétition de la partie 2 décrite précédemment, suivie d'une double citation et de certains caractéristiques, mais pas de double citation, ni comas.
    • La première partie de la part d'être suivie d'un coma.
    • Nota, le reste de la ligne n'a pas besoin d'être touché
  • ta _ va boucler sur :a si la commande précédente s/ a fait un certain changement.
7
F. Hauri

Une solution générale pouvant également gérer plusieurs virgules entre guillemets équilibrés nécessite une substitution imbriquée. Je mettez en œuvre une solution à Perl, qui traite toutes les lignes d'une entrée donnée et seulement des virgules de substitution dans toutes les autres citations:

Perl -pe 's/ "  (.+?  [^\\])  "               # find all non escaped 
                                              # quoting pairs
                                              # in a non-greedy way

           / ($ret = $1) =~ (s#,##g);         # remove all commas within quotes
             $ret                             # substitute the substitution :)
           /gex'

ou bref

Perl -pe 's/"(.+?[^\\])"/($ret = $1) =~ (s#,##g); $ret/ge'

Vous pouvez soit pipeler le texte que vous souhaitez traiter à la commande ou spécifier le fichier texte à traiter comme dernier argument de ligne de commande.

5
user1146332

Vos deuxième citations sont égarées:

sed -e 's/\(".*\),\(.*"\)/\1 \2/g'

De plus, l'utilisation d'expressions régulières tend à correspondre à la partie la plus longue possible du texte, ce qui signifie que cela ne fonctionnera pas si vous avez plus d'un champ cité dans la chaîne.

Une manière qui gère plusieurs champs cités dans SED

sed -e 's/\(\"[^",]\+\),\([^",]*\)/\1 \2/g' -e 's/\"//g'

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 la SED devrait être répétée autant de fois que la teneur maximale de la virgule dans un seul champ, ou jusqu'à ce qu'elle soit. ne change pas du tout la sortie.

En cours d'exécution SED avec plus d'une expression devrait être plus efficace que plusieurs processus SED fonctionnant et un "TR" fonctionnant avec des tuyaux ouverts.

Cependant, cela peut avoir des conséquences indésirables si l'entrée n'est pas correctement formatée. I.e. citations imbriquées, citations non définies.

En utilisant l'exemple de fonctionnement:

echo '123,"ABC, DEV 23",345,534,"some more, comma-separated, words",202,NAME' \
| sed -e 's/\(\"[^",]\+\),\([^",]*\)/\1 \2/g' \
-e 's/\(\"[^",]\+\),\([^",]*\)/\1 \2/g' -e 's/\"//g'

Sortir:

123,ABC  DEV 23,345,534,some more  comma-separated  words,202,NAME
3
Didi Kohen

En Perl - vous pouvez utiliser Text::CSV Pour analyser cela, et faites-le trivialement:

#!/usr/bin/env Perl
use strict;
use warnings;

use Text::CSV; 

my $csv = Text::CSV -> new();

while ( my $row = $csv -> getline ( \*STDIN ) ) {
    #remove commas in each field in the row
    $_ =~ s/,//g for @$row;
    #print it - use print and join, rather than csv output because quotes. 
    print join ( ",", @$row ),"\n";
}

Vous pouvez imprimer avec Text::CSV Mais il a tendance à préserver des citations si vous le faites. (Bien que je suggérais - plutôt que stripping citations pour votre sortie, vous pouvez simplement analyser en utilisant Text::CSV en premier lieu).

2
Sobrique

En utilisant python

''.join([item if index % 2 == 0 else re.sub(',', '', item) for index, item in enumerate(row.split('"')) ])
0
LoMaPh

J'ai créé une fonction pour boucler à travers tous les caractères de la chaîne.
[.____] Si le personnage est une citation, le chèque (b_in_qt) est marqué true.
[.____] tandis 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.

FUNCTION f_replace_c (str_in  VARCHAR2) RETURN VARCHAR2 IS
str_out     varchar2(1000)  := null;
str_chr     varchar2(1)     := null;
b_in_qt     boolean         := false;

BEGIN
    FOR x IN 1..length(str_in) LOOP
      str_chr := substr(str_in,x,1);
      IF str_chr = '"' THEN
        if b_in_qt then
            b_in_qt := false;
        else
            b_in_qt := true;
        end if;
      END IF;
      IF b_in_qt THEN
        if str_chr = ',' then
            str_chr := ' ';
        end if;
      END IF;
    str_out := str_out || str_chr;
    END LOOP;
RETURN str_out;
END;

str_in := f_replace_c ("blue","cat,dog,horse","",yellow,"green")

RESULTS
  "blue","cat dog horse","",yellow,"green"
0
user143598