Qual é uma boa maneira de filtrar um arquivo de texto para remover linhas vazias?

11

Eu tenho um arquivo .csv (em um mac) que possui várias linhas vazias, por exemplo:

"1", "2", "lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum 

lorem ipsum ","2","3","4"
"1", "2", "lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum 

lorem ipsum ","2","3","4"

Que eu quero converter para:

"1", "2", "lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum ","2","3","4"
"1", "2", "lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum  lorem ipsum ","2","3","4"

Eu sei que deve haver uma fila, mas não sei awk ou sed. Algumas dicas muito apreciadas!

pitosalas
fonte
1
De acordo com esse exemplo, você realmente deseja remover quebras de linha incorporadas dos campos. Isso está correto? Em outras palavras, existem 6 linhas de entrada e deve haver 2 linhas de saída?
manatwork
Sim, é exatamente disso que estou tentando me livrar: novas linhas incorporadas dentro de uma sequência de caracteres citada.
Pitosalas #
Então, o que você precisa é algo que remova novas linhas entre aspas. Isso vai ser um pouco mais complicado, porque você precisa de expressões regulares de várias linhas.
Tongpu

Respostas:

11

Você pode usar o modo grep -v(correspondência invertida) para fazer isso:

grep -v '^$' old-file.csv > new-file.csv

Observe que esses arquivos precisam ser diferentes, devido ao modo como os redirecionamentos de shell funcionam. O arquivo de saída é aberto (e esvaziado) antes da leitura do arquivo de entrada. Se você tiver moreutils (não por padrão no Mac OS X), poderá usar spongepara contornar isso:

grep -v '^$' file.csv | sponge file.csv

Mas é claro que você terá mais dificuldade em voltar se algo der errado.

Se você "linhas em branco" realmente pode conter espaços (parece que sim), então você pode usar isso:

egrep -v '^[[:space:]]*$' old-file.csv > new-file.csv

Isso ignorará as linhas em branco e as linhas que contêm apenas espaços em branco. É claro que você pode fazer a mesma spongetransformação nele.

derobert
fonte
Obrigado .... Não excluiu nenhuma linha vazia ... Talvez o ^ $ não esteja correspondendo? Mas as linhas estão vazias de acordo com o meu conhecimento. Lembre-se de que este é um cdv criado pelo excel em um mac ... Isso diz alguma coisa? (Não fuja gritando porque eu disse Excel :)
pitosalas
@pitosalas Eles provavelmente não são linhas vazias. Tente mudá-lo para egrep -v '^[[:space:]]*$'... nota grep -> egrep e o novo padrão estranho
derobert
Não funcionou. Eliminado um monte de aspas duplas e fez uma bagunça ...
pitosalas
@pitosalas Não tenho certeza de como ele excluiria aspas duplas. Só deve poder excluir espaços em branco. E, de fato, é o que ele faz quando eu testá-lo sobre os dados de exemplo que você postou ...
derobert
@pitosalas você poderia verificar se qualquer destes comandos cospe algo que parece razoáveis (em oposição ao jargão): iconv -f utf16le file.csv | headouiconv -f utf16be file.csv | head
derobert
8

A opção mais fácil é justa grep .. Aqui, o ponto significa "corresponder a qualquer coisa"; portanto, se a linha estiver vazia, ela não corresponderá. Caso contrário, imprime toda a linha como está.

Onturenio
fonte
6

Para remover linhas vazias, no lugar , com ksh93:

sed '/./!d' file 1<>; file

O <>;operador de redirecionamento é específico para ksh93 e é o mesmo que o <>operador padrão , exceto que o ksh trunca o arquivo após o término do comando.

sed '/./!d'é uma maneira complicada de escrever grep ., mas infelizmente o GNU grep reclama pelo menos se o seu stdout apontar para o mesmo arquivo que o stdin. Você diria que alguém poderia escrever:

grep . file | cat 1<>; file

Mas, infelizmente, há um bug no ksh93 (pelo menos minha versão (93u +)), pois o arquivo parece estar truncado para um comprimento zero nesse caso.

grep . file | { cat; } 1<>; file

Parece contornar esse bug, mas agora é muito mais complicado do que o comando sed.

Stéphane Chazelas
fonte
Combine suas respostas em uma entrada bem formatada com um guia rápido para quando cada solução deve ser empregada. As diferentes abordagens para diferentes problemas, todas reunidas em respostas flutuantes, tornaram essa pergunta um pouco desastrosa de se ler.
Caleb
@ Caleb, tudo se resume à questão de não ser clara, então todas as respostas de todos são para diferentes interpretações da pergunta. Para cada resposta, tentei dizer qual pergunta ela tenta responder.
Stéphane Chazelas 5/12/12
Apenas FYI: Tentei o awk '/./' file 1<>; fileque funcionou. Para mim, isso é ainda mais clara do quesed '/./!d'
grebneke
5

Aqui está uma Perllinha para isso:

perl -pi -e 's/^\s*\n//' yourfile

EDIT: Código aprimorado com base nos comentários de ruakh abaixo.

Joseph R.
fonte
1
Ouperl -ni -e '/./ and print' yourfile
derobert
1
@peterph $é uma âncora (largura zero), portanto exclui a nova linha. Quanto ao espaço supérfluo, é a razão pela qual eu adicionei o /xque eu não queria Perltentar interpolação `$ \` para o regex
Joseph R.
1
Você não precisa do $, dado que você tem o \n. (Como alternativa - você não precisa do \n, dado que possui o \s*e o $; mas acho que s/^\s*\n//deixa mais claro que a nova linha foi removida.) Você também não precisa do /m; não tem efeito sobre este comando. E uma vez que você se livrar do $espaço, não precisará dele /x.
Ruakh
1
@JosephR .: O \npróprio pode ser removido; o que você não pode fazer é remover tanto o $ e a \n. Então s/^\s*//, teria o problema que você descreve, mas s/^\s*$//ficaria bem, por causa do \s*e do $. (Você vê o que eu quero dizer?)
ruach
1
@JosephR .: O que acontece é que $ pode corresponder antes de uma nova linha (desde que o /msinalizador esteja ativado ou a nova linha seja o último caractere da string, ou ambos), mas também pode corresponder ao final da string. Por exemplo, "abc" =~ m/^abc$/é verdade. No caso de \s*$, o \s*é ganancioso o suficiente para consumir a nova linha e, em seguida, $corresponde ao final da cadeia. (Mas eu acho que s/^\s*\n//é mais claro, de qualquer maneira, para que a sua resposta é muito bem como é agora.)
ruach
5

Com base no esclarecimento nos comentários da sua pergunta, algo como:

awk -v RS= -v ORS= 1

pode fazer o que quiser.

Um separador de registros vazio é um caso especial que informa awkque os registros devem ser parágrafos (separados por sequências de linhas vazias). Definir o separador de registros de saída como uma sequência vazia também significa que o conteúdo desses parágrafos (sem os separadores) deve ser concatenado. 1é apenas uma condição verdadeira para imprimir todos os registros.

No entanto, isso omitiria a nova linha à direita, para que você pudesse:

awk -v RS= -v ORS= '1;END{if (NR) printf "\n"}'
Stéphane Chazelas
fonte
3

Sei que isso seria mais fácil se eu desse o arquivo, mas infelizmente ele continha informações confidenciais que não consegui compartilhar. Enquanto isso, escrevi-me um script em rubi que parecia fazer o truque:

require 'csv'
c = CSV.open("outfile1.csv", "w")
CSV.foreach("data.csv", :encoding => 'windows-1251:utf-8') do |row|
  row = row.map { |a| a.class == String ? a.gsub(/\r/, '') : a}
  c << row
end
c.close

Obrigado a todos por ajudar!

pitosalas
fonte
2
awk '
    length == 0 {next} 
    /^[^"]/ && /"$/ {print; next} 
    {printf("%s", $0)}
' filename

produz

"1", "2", "lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum ","2","3","4"
"1", "2", "lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum ","2","3","4"
Glenn Jackman
fonte
2

Encontrei uma idéia para uma possível solução no stackoverflow .

sed -i ':a;N;$!ba;s/[^"]\n\s*\n/ /g' file.csv

Provavelmente, você deve fazer backup do seu arquivo csv antes de testá-lo, mas pelo menos para o exemplo que você forneceu, ele funciona perfeitamente.

Uma boa explicação sobre o funcionamento interno dessa expressão é oferecida na resposta, apenas a editei para procurar linhas que não terminam com a "( [^"]\n).

tongpu
fonte
1

Se, a partir de sua própria resposta, você deseja remover os caracteres de nova linha contidos nas cadeias citadas, você pode:

 perl -0777 -pe 's/".*?"/$_=$&;s:\n::g;$_/gse'

Você também pode usar o -isinalizador de perl para editar os arquivos no local .

 perl -0777 -pe 's/".*?"/$_=$&;s:\n::g;$_/gse' file1 file2...

Ou com o GNU awk:

 awk -v RS=\" 'NR%2==0 {gsub("\n","")}; {printf "%s", $0 RT}'

ou:

 awk -vRS=\" '1-NR%2{gsub("\n","")}{ORS=RT}1'

(se você estiver competindo pelo menor)

Note-se que aqueles assumir que não há são escapou caracteres aspas duplas na entrada.

Stéphane Chazelas
fonte
0

Parece que você deseja mais do que remover linhas vazias, mas remova cada sequência de 2 ou mais caracteres de nova linha.

O que você pode fazer com o perl:

perl -0777 -pe 's/\n{2,}//gs' file

Você também pode usar o -isinalizador de perl para editar os arquivos no local .

perl -0777 -pi -e 's/\n{2,}//gs' file1 file2...
Stéphane Chazelas
fonte
0

Existe uma maneira cada vez mais curta de remover linhas vazias em AWK:

awk 'NF' file

Mas, para obter a saída desejada, tudo o que é necessário é um liner simples:

awk 'NF {printf("%s ", $0); i++;} !(i % 2) {printf("\n");}' file

Explicação

Em AWK, uma linha vazia significa que a linha / registro não possui campos, ou seja, a NFvariável (Número de campos) é zero. O liner acima só será executado quando NF > 0, imprimindo todas as linhas, exceto as vazias.

O i++é o contador de linhas não vazias.

The !(i % 2)é usado para imprimir duas linhas consecutivas não vazias no caminho da saída desejada, ou seja, toda vez que um múltiplo de 2 é encontrado, a moduloinstrução !(i % 2)gera 1, o que encerra a concatenação de duas linhas não vazias.

Marcelo Augusto
fonte
Foi mal! Desculpe. Não li toda a pergunta e a saída desejada. A resposta está corrigida agora. Obrigado. :-)
Marcelo Augusto
0

Você pode usar o Vim no modo Ex:

ex -sc v/./d -cx b.csv
  1. v/./ encontre linhas vazias

  2. d excluir

  3. x salvar e fechar

Steven Penny
fonte