web-dev-qa-db-fra.com

Supprimer des guillemets autour d'entiers dans un fichier csv

Dans un gros fichier csv (> 1 Go), j'ai quelque chose comme:

"34432", "name", "0", "very long description"

mais au lieu de cela, j'aimerais avoir

34432, "name", 0, "very long description".

Je regardais sed mais cette tâche est hors de mon champ.

Des conseils pour y parvenir?

4
Balázs Németh

Utiliser Perl:

Perl -ne 's/"(\d+)"/$1/g; print' file.csv > new_file.txt

Tout le travail est fait par s/"(\d+)"/$1/g

  • s/patternA/patternB/ est utilisé pour remplacer patternA par patternB
  • alors Perl recherche un ou plusieurs chiffres \d+ entourés de guillemets doubles.
  • les parenthèses autour de (\d+) sont utilisées pour capturer le ou les chiffres et les réutiliser en tant que modèle de remplacement avec la variable spéciale Perl $1.
6
Sylvain Pineau

Une regex GNU sed qui devrait fonctionner dans ce cas est

sed -r 's/"([0-9]+)"/\1/g'    

Pour le sed pur, vous devez échapper aux parenthèses de regroupement et au modificateur +

sed 's/"\([0-9]\+\)"/\1/g'

Vous pouvez effectuer la substitution sur place avec certaines versions de sed, par exemple.

sed -ri 's/"([0-9]+)"/\1/g' file.csv

Vous pouvez également utiliser la classe POSIX [[:digit:]] à la place de la plage de caractères [0-9]

6
steeldriver

Votre description du problème n'est pas très spécifique. Je suppose que vous souhaitez supprimer les guillemets doubles autour des premier et troisième champs uniquement. Si tel est le cas, l'un d'entre eux devrait fonctionner:

  1. sed

    sed -r 's/^"([^"]+)"(\s*,\s*[^,]+)\s*,\s*"([^"]+)"/\1\2, \3/' file.csv
    

    Explication

    Le -r active les expressions régulières étendues, ce qui nous permet d'utiliser des parenthèses pour capturer des motifs sans avoir à les échapper. Nous faisons donc correspondre une citation au début de la ligne (^"), suivie d'un ou plusieurs caractères non-guillemets ([^"]+), puis de la citation de clôture, suivie de 0 ou de plusieurs espaces, un virgule, puis à nouveau 0 ou plusieurs espaces (\s*,\s*), puis un extrait de virgule jusqu'à la virgule suivante (ce qui définit le deuxième champ). Enfin, nous cherchons 0 ou plusieurs espaces, une virgule, et nous le remplaçons par le premier motif capturé (\1), puis le deuxième (\2), une virgule, un espace et le troisième.

  2. Perl

    Perl -pe 's/^"([^"]+)"(\s*,\s*[^,]+)\s*,\s*"([^"]+)"/$1$2, $3/; ' file.csv
    

    Explication

    -p signifie imprimer chaque ligne après avoir appliqué le script transmis par -e. Le script lui-même est fondamentalement le même regex que dans le sed ci-dessus. Seulement ici, les modèles capturés sont $1.

  3. awk

    awk -F, -v OFS="," '{gsub("\"","",$1)0gsub("\"","",$3);}1;' file.csv 
    

    Explication

    Le -F définit le séparateur de champ sur ,. OFS est le séparateur de champ de sortie qui est également réglé sur , pour que les lignes soient correctement imprimées. La gsub effectue la substitution en remplaçant tout " par rien puisque nous l'exécutons sur les 1er ($1) et 3ème champs ($3), il ne supprimera que les guillemets ces champs. Le 1; est simplement awk en abrégé pour "imprimer la ligne".

5
terdon

Solution python

Le petit script ci-dessous prend l'argument de ligne de commande file, itère chaque ligne de ce fichier et divise chaque ligne en une liste d'éléments en utilisant , comme séparateur. Chaque entrée est ensuite décochée et sa chaîne numérique vérifiée; si une chaîne est numérique, elle n'est pas citée.

#!/usr/bin/env python
import sys

with open(sys.argv[1]) as fp:
    for line in fp:
        new_vals = []
        vals = line.strip().split(',')
        for val in vals:
            val = val.strip().rstrip().replace('"','')
            if not val.isdigit(): 
               val = '"' + val  + '"'
            new_vals.append(val)
        print(",".join(new_vals))

Essai:

$ cat input.txt
"34432", "name", "0", "very long description" 
"1234", "othe name" , "42", "another description"
$ ./unquote_integers.py  input.txt                                       
34432,"name",0,"very long description"
1234,"othe name",42,"another description"

Notes complémentaires:

Il a été demandé dans les commentaires, pourquoi le script supprime les guillemets autour de chaque élément avant d'évaluer si l'élément est une chaîne numérique ou non. La raison principale en est que l’inclusion de guillemets doubles fera en sorte que des éléments comme "123" soient évalués à False, c’est-à-dire non numérique. Effectivement, nous devons évaluer le contenu des guillemets doubles d’une manière ou d’une autre. Maintenant, il existe une autre façon d’aborder ceci en prenant une tranche de liste de chaque valeur. Cependant, ce n’est pas mieux que d’utiliser .replace() depuis le début. Cela raccourcit le code, mais au moins dans ce cas, la brièveté d'un script n'est pas pertinente - notre objectif est de faire en sorte que le code fonctionne, et non le code-golf.

Voici la solution alternative avec des tranches de liste:

#!/usr/bin/env python
import sys

with open(sys.argv[1]) as fp:
    for line in fp:
        new_vals = []
        vals = line.strip().split(',')
        for val in vals:
            val = val.strip().rstrip() #remove extra spaces
            val = val.replace('"','') if val[1:-1].isdigit() else val
            new_vals.append(val)
        print(",".join(new_vals))
1
Sergiy Kolodyazhnyy