Como posso remover um novo caractere de linha em uma coluna específica em um arquivo CSV?

4

Eu tenho um arquivo CSV com mais de 150 colunas, com o novo caractere de linha como um separador de registro. O problema está em uma das colunas recebendo novos caracteres de linha. Para isso, quero removê-los.

Entrada:

001|Baker St.
London|3|4|7
002|Penny Lane
Liverpool|88|5|7

Saída:

001|Baker St. London|3|4|7
002|Penny Lane Liverpool|88|5|7
    
por Vicky 07.05.2018 / 13:02

7 respostas

7

Você pode usar sed para mesclar a linha seguinte na linha atual, contanto que a linha atual não contenha 4 | caracteres:

<file sed -e :1 -e 's/|/|/4;t' -e 'N;s/\n/ /;b1'

Algumas implementações de sed têm -i ou -i '' para editar arquivos no local ( -i.back para salvar o original com uma extensão .back ), portanto, com essas, você poderia:

sed -i -e :1 -e 's/|/|/4;t' -e 'N;s/\n/ /;b1' ./*.csv

Para editar todos os arquivos csv não ocultos no diretório atual.

O mesmo com comentários:

<file sed '
   :1
     s/|/|/4; # replace the 4th | with itself. Only useful when combined with
              # the next "t" command which branches off if the previous
              # substitution was successful
     t
     # we only reach this point if "t" above did not branch off, that is
     # if the pattern space does not contain 4 "|"s
     N; # append the next line to the pattern space
     s/\n/ /; # replace the newline with a space

   # and then loop again in case the pattern space still does not contain
   # 4 "|"s:
   b1'
    
por 07.05.2018 / 13:39
3

Confiando no formato do primeiro campo (assumindo que cada linha deve começar com um número):

awk 'NR == 1{ printf $0; next }
     { printf "%s%s", (/^[0-9]+/? ORS : ""), $0 }
     END{ print "" }' file.csv

A saída:

001|Baker St.London|3|4|7
002|Penny LaneLiverpool|88|5|7
    
por 07.05.2018 / 13:22
2

Outra solução awk do GNU depende de 4 vezes | por registro:

awk -v RS='([^|]+\|){4}[^|]+\n' '{gsub(/\n/," ",RT); print RT}' file

RS é definido de forma que o registro contenha os 4 separadores (mesmo com uma nova linha).

RT captura o registro definido por RS . gsub remove a nova linha no registro.

    
por 07.05.2018 / 16:35
1

Se podemos supor que qualquer linha com apenas dois campos deve ter sua nova linha final removida, você pode fazer o seguinte em Perl:

$ perl -F"\|" -lane '$#F==1 ? printf : print' file.csv 
001|Baker St.London|3|4|7
002|Penny LaneLiverpool|88|5|7

Aviso Importante : como apontado nos comentários de Stéphane Chazelas, isso pressupõe que sua entrada não contenha nenhum caractere % , pois, se isso acontecer, eles serão considerados como formato especificador para printf . Isso pode ter conseqüências não intencionais, variando de simplesmente imprimir uma saída incorreta a comer cargas de RAM, se sua entrada tiver algo parvo como %02147483600f%02147483600f%02147483600f%02147483600f .

Explicação

  • -a : torna perl como awk , dividindo cada linha de entrada no caractere fornecido por -F (portanto, um | aqui; que precisa ser escapado como \| , pois | significa OR em expressões regulares perl) e salvando o resultado como a matriz @F .
  • -l : remove as novas linhas à direita de cada linha de entrada e adiciona uma chamada 'n t cada print .
  • -ne : leia o arquivo de entrada linha a linha e aplique o script fornecido por -e em cada linha.
  • $#F==1 ? printf : print' : A variável $#F é o número de elementos na matriz @F , portanto, o número de campos. Isso, portanto, significa que se o número de campos for 1, então printf (que imprimirá a linha atual sem um caractere de nova linha, já que a existente foi removida por -l e printf não adiciona uma). Se o número de campos não for exatamente 1, print da linha (que, por causa do -l , adicionará uma nova linha).

A mesma coisa pode ser expandida para:

$ perl -e 'while($line=<STDIN>){
            chomp $line; 
            @fields=split(/\|/,$line); 
            if(scalar(@fields) == 2){
                print "$line";
            } 
            else{
                print "$line\n"
            }
           }' < file.csv 
001|Baker St.London|3|4|7
002|Penny LaneLiverpool|88|5|7

E uma versão ainda mais curta sugerida por @Sundeep nos comentários:

perl -F'\|' -ape 'chomp if $#F==1'
    
por 07.05.2018 / 16:08
0

se a primeira linha do seu CSV estiver correta, o código a seguir funcionará.

awk  'NR==1{printf "%s",$0; gsub(/[^|]/,""); nlast=n=length($0); next;} nlast==n{printf "\n";nlast=0} {printf "%s",$0; gsub(/[^|]/,""); nlast+=length($0)} END{print ""}'  file_201805072030.csv > temp.csv && mv -f temp.csv file_201805072030.csv

se nenhuma das linhas estiver correta, e se você quiser reorganizar com 5 colunas

awk  'NR==1{printf "%s",$0; gsub(/[^|]/,""); nlast=n=4; next;} nlast==n{printf "\n";nlast=0} {printf "%s",$0; gsub(/[^|]/,""); nlast+=length($0)} END{print ""}' file_201805072030.csv > temp.csv && mv -f temp.csv file_201805072030.csv
    
por 07.05.2018 / 13:39
0

Com a ferramenta sed , você pode fazer como mostrado:

sed  -i  -e '
             /^\(\([^|]*|\)\{2\}\)*[^|]*$/b
             N;s/\n/ /
             s/^/\n/;D
   '     . /*.csv

Explicação:

  1. Ramifique para finalizar o processamento do código sed para os dados do espaço de padrão atual quando um número par de canais for encontrado.

  2. Ou vá buscar a próxima linha e anexe-a ao espaço do padrão.

  3. Agora, reaplique o código sed novamente no espaço padrão.

     perl -i.BAK    -lpe '
         $\ = ( $k += tr/|/|/ ) =~ /[24680]$/ ? "\n" : " ";
      '     . /*.csv 
    
  4. $k é um contador de números em execução se os canais forem vistos a qualquer momento.

  5. Até mesmo nós imprimimos com uma nova linha, caso contrário, juntamos as linhas com um espaço.

por 07.05.2018 / 20:25
-1
awk '/Baker/{printf "%s ",$0; getline; printf "%s\n", $0} \
/Penny/{printf $0; getline; printf "%s\n", $0}' file

001|Baker St. London|3|4|7
002|Penny LaneLiverpool|88|5|7
    
por 11.05.2018 / 02:39