Remover aspas em torno de números inteiros em um arquivo csv

4

Em um arquivo csv grande (> 1 gb), tenho algo como

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

mas em vez disso gostaria de ter

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

Eu estava olhando para sed , mas essa tarefa está fora do meu escopo.

Algum conselho sobre como conseguir isso?

    
por Balázs Mária Németh 08.04.2014 / 17:19

4 respostas

6

Usando o perl:

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

Todo o trabalho é feito por s/"(\d+)"/$1/g onde

  • s/patternA/patternB/ é usado para substituir patternA por patternB
  • , em seguida, perl procura por um ou mais dígitos \d+ entre aspas duplas.
  • os parênteses em torno de ( \d+ ) são usados para capturar o (s) dígito (s) e reutilizá-los como um padrão de substituição com perl especial variável $1 . / li>
por Sylvain Pineau 08.04.2014 / 17:21
6

Um regex do GNU sed que deve funcionar para este caso é

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

Para o sed puro, você precisa escapar dos parênteses de agrupamento e + modifier

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

Você pode realizar a substituição no local com algumas versões do sed, por exemplo.

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

Você também pode usar a classe POSIX [[:digit:]] no lugar do intervalo de caracteres [0-9]

    
por steeldriver 08.04.2014 / 17:41
5

Sua descrição do problema não é muito específica. Eu estou supondo que você deseja remover as aspas duplas em torno dos campos 1 e 3 apenas. Em caso afirmativo, qualquer um deles deve funcionar:

  1. sed

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

    Explicação

    O -r ativa expressões regulares estendidas, permitindo usar parênteses para capturar padrões sem precisar deles. Então, combinamos uma citação no início da linha ( ^" ), seguida por um ou mais caracteres não citados ( [^"]+ ), depois a citação de fechamento, seguida por 0 ou mais espaços, uma vírgula e 0 ou mais espaços novamente ( \s*,\s* ), então um trecho de sem-vírgulas até a próxima vírgula (isso define o segundo campo). Finalmente, procuramos por 0 ou mais espaços, uma vírgula, e substituímos com o primeiro padrão capturado ( ), depois o segundo ( ), uma vírgula, um espaço e o terceiro.

  2. Perl

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

    Explicação

    O -p significa imprimir todas as linhas depois de aplicar o script passado por -e . O script em si é basicamente o mesmo regex que no sed acima. Só aqui, os padrões capturados são $1 .

  3. awk

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

    Explicação

    O -F define o separador de campo como , . OFS é o separador do campo de saída que também está configurado para , , de forma que as linhas sejam impressas corretamente. O gsub faz a substituição, substituindo todos os " por nada, uma vez que executamos no primeiro ( $1 ) e no terceiro campo ( $3 ) ele só removerá as aspas desses campos. O 1; é apenas awk de abreviação para "imprimir a linha".

por terdon 08.04.2014 / 17:50
1

Solução Python

O pequeno script abaixo pega o argumento de linha de comando, faz uma iteração sobre cada linha desse arquivo e divide cada linha na lista de itens usando , como separador. Cada entrada é, então, sem aspas e verificada por ser uma string numérica; se uma string for numérica, ela será deixada sem aspas.

#!/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))

Execução de teste:

$ 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"

Notas adicionais :

Foi perguntado nos comentários, porque o script remove as aspas duplas em torno de cada item antes de avaliar se o item é uma string numérica ou não. A principal razão para isso é que a inclusão de aspas duplas fará com que um item como "123" avalie para False , ou seja, não numérico. Efetivamente, precisamos avaliar o que está dentro das aspas duplas de alguma forma. Agora, existe uma maneira alternativa de abordar isso usando a lista de fatias de cada valor. No entanto, isso não é melhor do que usar .replace() desde o início. Ele torna o código mais curto, mas, pelo menos nesse caso, a falta de um script é irrelevante - nosso objetivo é fazer o código funcionar, não codificá-lo com códigos.

Veja a solução alternativa com fatias de lista:

#!/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))
    
por Sergiy Kolodyazhnyy 25.02.2017 / 23:20