Qual è un buon modo per filtrare un file di testo per rimuovere le righe vuote?


11

Ho un file .csv (su un mac) che ha un sacco di righe vuote, ad esempio:

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

In cui desidero convertire:

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

So che ci dev'essere una fodera ma non conosco awk o sed. Qualche consiglio molto apprezzato!


1
Secondo tale esempio, si desidera effettivamente rimuovere le interruzioni di riga incorporate dai campi. È corretto? In altre parole, ci sono 6 linee di input e dovrebbero essere 2 linee di output?
arte

Sì, è esattamente quello che sto cercando di liberarmi: newline incorporate all'interno di una stringa tra virgolette.
Pitosalas,

Quindi ciò di cui hai bisogno è qualcosa che rimuova le nuove righe tra virgolette. Sarà un po 'più complicato, perché hai bisogno di regex multilinea.
tongpu,

Risposte:


11

Puoi usare la modalità -v(inverti corrispondenza) di grep per fare questo:

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

Si noti che quelli devono essere file diversi, a causa del funzionamento dei reindirizzamenti della shell. Il file di output viene aperto (e svuotato) prima di leggere il file di input. Se hai più programmi (non di default su Mac OS X), puoi usare spongeper aggirare questo:

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

Ma, naturalmente, è più difficile tornare indietro se qualcosa va storto.

Se in realtà le "righe vuote" possono contenere spazi (sembra che lo facciano), puoi invece usare questo:

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

Ciò ignorerà le righe vuote e le righe contenenti solo spazi bianchi. Ovviamente puoi fare la stessa spongetrasformazione su di esso.


Grazie .... Non hai eliminato nessuna riga vuota ... Forse ^ $ non corrisponde? Ma le righe sono vuote al meglio delle mie conoscenze. Ricorda che questo è un cdv creato da Excel su un Mac ... Dice qualcosa? (Non scappare urlando perché ho detto Excel :)
pitosalas,

@pitosalas Probabilmente non sono linee vuote. Prova a cambiarlo in egrep -v '^[[:space:]]*$'... nota grep -> egrep e il nuovo strano modello
derobert

Non ha funzionato.
Ho

@pitosalas Non sono sicuro di come eliminare le doppie virgolette. Dovrebbe essere in grado di eliminare solo gli spazi bianchi. E infatti, è quello che fa quando lo collaudo sui dati di esempio che hai pubblicato ...
derobert,

@pitosalas potresti verificare se uno di questi comandi sputa qualcosa che sembra ragionevole (al contrario di incomprensibile): iconv -f utf16le file.csv | headoppureiconv -f utf16be file.csv | head
derobert

8

L'opzione più semplice è solo grep .. Qui, il punto significa "abbina qualsiasi cosa", quindi se la linea è vuota, non viene abbinata. Altrimenti stampa l'intera riga così com'è.


6

Per rimuovere le righe vuote, sul posto , con ksh93:

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

L' <>;operatore di reindirizzamento è specifico di ksh93 ed è uguale <>all'operatore standard tranne per il fatto che ksh tronca il file dopo che il comando è terminato.

sed '/./!d'è un modo contorto di scrivere grep ., ma sfortunatamente GNU grep si lamenta almeno se il suo stdout punta allo stesso file del suo stdin. Diresti che si potrebbe scrivere:

grep . file | cat 1<>; file

Ma sfortunatamente, c'è un bug in ksh93 (almeno la mia versione (93u +)), in quanto il file sembra essere troncato a zero in quel caso.

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

Sembra aggirare quel bug, ma ora è molto più contorto del comando sed.


Combina le tue risposte in una voce ben formattata con una guida rapida su quando utilizzare ogni soluzione. I diversi approcci ai diversi problemi, tutti confusi in risposte fluttuanti, hanno reso questa domanda un po 'un disastro da leggere.
Caleb,

@Caleb, tutto si riduce alla domanda molto poco chiara, quindi tutte le risposte di tutti sono per interpretazioni diverse della domanda. Per ogni risposta, ho cercato di dire a quale domanda tenta di rispondere.
Stéphane Chazelas,

Cordiali saluti: Ho provato awk '/./' file 1<>; fileche ha funzionato. Per me, è ancora più chiaro dised '/./!d'
grebneke,

5

Ecco un Perlone-liner per questo:

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

EDIT: codice migliorato basato sui commenti di ruakh di seguito.


1
Oppureperl -ni -e '/./ and print' yourfile
derobert,

1
@peterph $è un ancoraggio (ovvero larghezza zero) quindi esclude la nuova riga. Per quanto riguarda lo spazio superfluo, è la ragione per cui ho aggiunto /xche non volevo Perlprovare a interpolare `$ \` nella regex
Joseph R.,

1
Non hai bisogno del $, dato che hai il \n. (In alternativa - non hai bisogno di \n, dato che hai il \s*e il $; ma penso s/^\s*\n//che chiarisca che la nuova riga viene rimossa.) Inoltre non hai bisogno del /m; non ha alcun effetto su questo comando. E una volta che ti libererai di $e dello spazio, non avrai bisogno di /x.
ruakh,

1
@JosephR .: lo \nstesso può essere rimosso; quello che non puoi fare è rimuovere sia il $ che il \n. Quindi s/^\s*//avrebbe il problema che descrivi, ma s/^\s*$//andrebbe bene, a causa del \s*e il $. (
Capisci

1
@JosephR .: Ciò che accade è che $ può corrispondere prima di una nuova riga (a condizione che il /mflag sia abilitato o che la nuova riga sia l'ultimo carattere della stringa o di entrambi), ma può anche corrispondere alla fine della stringa. Ad esempio, "abc" =~ m/^abc$/è vero. Nel caso di \s*$, \s*è abbastanza avido da divorare la nuova riga, e quindi $corrisponde alla fine della stringa. (Ma penso che s/^\s*\n//sia più chiaro, comunque, quindi la tua risposta va bene come è ora.)
ruakh

5

Sulla base del chiarimento nei commenti alla tua domanda, qualcosa come:

awk -v RS= -v ORS= 1

può fare quello che vuoi.

Un separatore di record vuoto è un caso speciale che indica awkche i record devono essere paragrafi (separati da sequenze di righe vuote). L'impostazione del separatore del record di output anche sulla stringa vuota significa che il contenuto di quei paragrafi (senza i separatori) deve essere concatenato. 1è solo una vera condizione per stampare ogni record.

Ciò ometterebbe la nuova riga finale, quindi potresti fare:

awk -v RS= -v ORS= '1;END{if (NR) printf "\n"}'

3

So che sarebbe stato più semplice se avessi dato il file, ma sfortunatamente conteneva informazioni riservate che non potevo condividere. Nel frattempo mi scrissi una sceneggiatura ruby ​​che sembrava fare il trucco:

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

Grazie a tutti per l'aiuto!


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

produce

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

2

Ho trovato un'idea per una possibile soluzione su StackOverflow .

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

Probabilmente dovresti eseguire il backup del tuo file CSV prima di testarlo, ma almeno per l'esempio che hai fornito funziona perfettamente.

Una buona spiegazione sul funzionamento interno di questa espressione è offerta nella risposta, l'ho appena modificata per cercare linee che non finiscono con un "( [^"]\n).


1

Se, dalla tua stessa risposta, desideri rimuovere i caratteri di nuova riga contenuti nelle stringhe tra virgolette, puoi fare:

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

Puoi anche usare usa il -iflag di perl per modificare i file sul posto .

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

O con GNU awk:

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

o:

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

(se stai competendo per il più corto)

Si noti che quelli presumono che non ci siano caratteri di virgolette doppie di escape nell'input.


0

Sembra in effetti che tu voglia più di rimuovere le righe vuote, ma rimuovi ogni sequenza di 2 o più caratteri di nuova riga.

Cosa che potresti fare con Perl:

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

Puoi anche usare usa il -iflag di perl per modificare i file sul posto .

perl -0777 -pi -e 's/\n{2,}//gs' file1 file2...

0

Esiste un modo sempre più breve di rimuovere le righe vuote in AWK:

awk 'NF' file

Ma per ottenere l'output desiderato, tutto ciò che serve è una semplice riga:

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

Spiegazione

In AWK, una riga vuota indica che la riga / il record non ha campi, ovvero la NFvariabile (Numero di campi) è zero. L'unico liner sopra verrà eseguito solo quando NF > 0, stampando tutte le linee, ma quelle vuote.

Il i++contatore delle righe non vuote è.

Il !(i % 2)è utilizzato per stampare due linee non vuote consecutive in modo del vostro output desiderato, cioè, ogni volta che viene trovato un multiplo di 2, le moduloistruzione !(i % 2)rese 1, quale termina la concatenazione di due linee non vuote.


Colpa mia! Scusate. Non ho letto tutta la sua domanda e l'output desiderato. La risposta è stata risolta ora. Grazie. :-)
Marcelo Augusto

0

Puoi usare Vim in modalità Ex:

ex -sc v/./d -cx b.csv
  1. v/./ trova righe vuote

  2. d Elimina

  3. x salva e chiudi

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.