vim regex procura e substitui

2

Estou tentando substituir parte de uma string em um arquivo

Por exemplo, eu tenho um arquivo csv.

r1,col1,col2,35,000,col4,col5
r2,col1,col2,1,000,col4,col5
r3,col1,col2,325.33,col4,col5
r4,col1,col2,4,325.33,col4,col5

Essencialmente eu quero substituir o, no que parece ser col3 acima. Enquanto preserva os primeiros números x, parece o seguinte:

r1,col1,col2,35000,col4,col5
r2,col1,col2,1000,col4,col5
r3,col1,col2,325.33,col4,col5
r4,col1,col2,4325.33,col4,col5

normalmente eu executaria

:%s/\,[0-9]*\,/\,/g

Mas quando eu corro, recebo

r1,col1,col2,000,col4,col5
r2,col1,col2,000,col4,col5
r3,col1,col2,325.33,col4,col5
r4,col1,col2,325.33,col4,col5

O que devo usar na segunda parte da minha substituição para obter o resultado desejado.

    
por Mark D 02.11.2017 / 21:08

4 respostas

1

Você provavelmente quer :%s/\v(([^,]*,){3})([0-9]+),([0-9])// .

Seu objetivo é remover uma vírgula do quarto campo, se presente, sem remover vírgulas em outro lugar e sem remover nenhum outro texto. O fator complicador é que as vírgulas também são usadas como separadores de campo. Para resolver o problema, você deve considerar o que sabe sobre as condições sob as quais as vírgulas podem aparecer dentro de um campo. Afinal, sem mais restrições, seus registros são ambíguos.

É tentador considerar qualquer vírgula abrangendo dois dígitos como assunto para remoção, mas isso não funcionará. Sua amostra de entrada mostra que você pode ter um final de campo com um dígito e o próximo campo começa com um ( col2,35,000 ).

Se você sabe que os três primeiros campos não contêm vírgulas, o problema se torna muito mais fácil, porque as três primeiras sequências de zero ou mais não-vírgulas seguidas por vírgulas podem ser ignoradas antes da remoção de vírgulas. Então a questão é como descobrir quando o quarto campo terminou. Você deve se perguntar se deseja remover vírgulas múltiplas do quarto campo, ou se é sempre sem vírgulas ou uma vírgula.

Eu assumirei, para o bem desta resposta, que o quarto campo contém no máximo uma vírgula que deve ser removida. Além disso, assumirei que a vírgula aparece após um ou mais dígitos e antes de pelo menos um dígito. Então você pode usar isso no Vim:

:%s/\v(([^,]*,){3})([0-9]+),([0-9])//

Ou, se você preferir usar Sed:

sed -r 's/(([^,]*,){3})([0-9]+),([0-9])//' filename.csv

Como funciona

A expressão regular (([^,]*,){3}) corresponde aos três primeiros campos e aos separadores de campos que os seguem, os quais você desejará manter sempre. [^,] corresponde a qualquer caractere único, mas , . O * depois faz com que zero ou mais deles sejam correspondidos em vez de exatamente um. O , depois disso corresponde à vírgula real que segue este campo de não-vírgulas. Isso tudo é agrupado com ( ) e o {3} aplicado a ele faz com que seja correspondido três vezes em vez de uma vez. Então essa coisa toda é agrupada para que possa ser acessada com . (O grupo interno também captura e poderia ser acessado como .)

Em seguida, ([0-9]+) corresponde a um ou mais dígitos ( + ) ( [0-9] ) e captura a correspondência ( ( ) ) para que possa ser acessada como . O caractere , corresponde a uma vírgula literal; essa é a parte que não iremos manter. Então, ([0-9]) captura um único dígito para que possa ser acessado como .

Você pode tornar a expressão regular um pouco mais simples usando um único grupo para e , ou seja, (([^,]*,){3}[0-9]+) . Evitei isso porque sinto que isso esconde a estrutura dos seus registros - que eles são compostos de campos separados por vírgulas - mas não há nada de errado em fazer dessa maneira. Se você fez isso, se tornaria , então no padrão de substituição você usaria em vez de .

Por fim, o \v no início do Vim regex e o -r passado para o sed serve para permitir que você use a sintaxe de expressão regular estendida. É por isso que consegui escrever ( e ) em vez de \( e \) e + em vez de \+ .

    
por 02.11.2017 / 22:03
1

Você pode fazer isso a partir do vim usando o seguinte regex:

 %s/\([^,]\+,\)\{3}[^,]*\zs,\ze[^,]*\(,[^,]\+\)\{2}//

Aqui está uma explicação:

  • \([^,]\+,\)\{3} corresponde exatamente a 3 campos csv e as vírgulas a seguir.

  • \(,[^,]\+\)\{2} corresponde exatamente a 2 campos csv e às vírgulas anteriores.

  • O que está no meio dessas duas expressões capturará o campo do qual uma vírgula deve ser removida.

por 02.11.2017 / 22:02
0
$ sed 's/,\([0-9]\+\),\([0-9]\+\)/,/' input
r1,col1,col2,35000,col4,col5
r2,col1,col2,1000,col4,col5
r3,col1,col2,325.33,col4,col5
r4,col1,col2,4325.33,col4,col5

Para explicar a expressão usada:

  NODE                     EXPLANATION
  ,                        ','
  (                        group and capture to :
    [0-9]+                   any character of: '0' to '9' (1 or more
                             times (matching the most amount
                             possible))
  )                        end of 
  ,                        ','
  (                        group and capture to :
    [0-9]+                   any character of: '0' to '9' (1 or more
                             times (matching the most amount
                             possible))
  )                        end of 

Em seguida, substituímos a correspondência por , .

    
por 02.11.2017 / 21:14
0

Você pode usar awk para esta tarefa. Este script pode processar várias vírgulas na quarta coluna. É difícil processar este caso (várias vírgulas) usando o vim , eu acho. Mas é fácil usando awk .

Observação: esta solução somente para seis colunas (também estou contando a coluna r1 ).

awk '
BEGIN {
    FS = ",";
    OFS = ",";
}
{
    accum = "";
    for(i = 4; i < NF - 1; i++) {
        accum = accum $i;       
    }

    print $1, $2, $3, accum, $(NF - 1), $NF;
}' input.txt

Entrada (linhas, com mais de uma vírgula no campo de destino, foram adicionadas para teste)

r1,col1,col2,35,000,col4,col5
r2,col1,col2,1,000,col4,col5
r3,col1,col2,325.33,col4,col5
r4,col1,col2,4,325.33,col4,col5
r5,col1,col2,4,325,250.33,col4,col5
r6,col1,col2,4,100,325,250.33,col4,col5

Resultado

r1,col1,col2,35000,col4,col5
r2,col1,col2,1000,col4,col5
r3,col1,col2,325.33,col4,col5
r4,col1,col2,4325.33,col4,col5
r5,col1,col2,4325250.33,col4,col5
r6,col1,col2,4100325250.33,col4,col5
    
por 03.11.2017 / 18:55