Apa cara yang baik untuk memfilter file teks untuk menghapus baris kosong?


11

Saya memiliki file .csv (di mac) yang memiliki banyak baris kosong, misalnya:

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

Yang ingin saya konversi menjadi:

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

Saya tahu pasti ada satu kalimat tetapi saya tidak tahu awk atau sed. Setiap tips sangat dihargai!


1
Menurut sampel itu Anda benar-benar ingin menghapus jeda baris tertanam dari bidang. Apakah itu benar? Dengan kata lain, ada 6 jalur input dan harus 2 jalur output?
manatwork

Ya, itulah yang saya coba singkirkan: menyematkan baris baru di dalam string yang dikutip.
pitosalas

Jadi yang Anda butuhkan adalah sesuatu yang menghilangkan baris baru di dalam tanda kutip. Itu akan menjadi sedikit lebih rumit, karena Anda memerlukan regil multiline.
tongpu

Jawaban:


11

Anda dapat menggunakan -vmode grep (invert match) untuk melakukan ini:

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

Perhatikan bahwa itu harus file yang berbeda, karena cara kerja pengalihan shell. File output dibuka (dan dikosongkan) sebelum file input dibaca. Jika Anda memiliki lebih banyakutils (tidak secara default pada Mac OS X), Anda dapat menggunakannya spongeuntuk mengatasi ini:

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

Tapi tentu saja, maka Anda akan lebih sulit untuk kembali jika terjadi kesalahan.

Jika Anda "baris kosong" sebenarnya mungkin berisi spasi (sepertinya memang begitu), maka Anda dapat menggunakan ini sebagai gantinya:

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

Itu akan mengabaikan garis kosong dan juga baris yang hanya berisi spasi putih. Tentu saja Anda dapat melakukan spongetransformasi yang sama .


Terima kasih .... Tidak menghapus baris kosong ... Mungkin ^ $ tidak cocok? Tapi garis-garisnya kosong sepengetahuan saya. Ingat ini adalah cdv yang dibuat oleh excel di mac ... Apakah itu mengatakan sesuatu? (Jangan lari berteriak karena saya katakan Excel :)
pitosalas

@pitosalas Mereka mungkin bukan baris kosong. Coba ubah ke egrep -v '^[[:space:]]*$'... perhatikan grep -> egrep dan pola baru yang aneh
derobert

Tidak bekerja Menghapus banyak tanda kutip ganda dan mengacaukan ...
pitosalas

@pitosalas Saya tidak yakin bagaimana cara menghapus tanda kutip ganda. Seharusnya hanya bisa menghapus spasi putih. Dan memang, itulah yang dilakukannya ketika saya mengujinya pada contoh data yang Anda posting ...
derobert

@pitosalas dapatkah Anda memeriksa apakah salah satu dari perintah ini mengeluarkan sesuatu yang terlihat masuk akal (tidak seperti omong kosong): iconv -f utf16le file.csv | headatauiconv -f utf16be file.csv | head
derobert

8

Opsi termudah adalah adil grep .. Di sini, titik berarti "cocokkan apa pun", jadi jika garisnya kosong, itu tidak cocok. Selain itu, ia mencetak seluruh baris apa adanya.


6

Untuk menghapus garis kosong, di tempat , dengan ksh93:

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

The <>;operator redirection khusus untuk ksh93 dan sama dengan standar <>Operator kecuali bahwa memotong ksh file setelah perintah telah dihentikan.

sed '/./!d'adalah cara berbelit-belit untuk menulis grep ., tetapi sayangnya GNU grep setidaknya mengeluh jika stdout-nya menunjuk ke file yang sama dengan stdin-nya. Anda bisa mengatakan orang bisa menulis:

grep . file | cat 1<>; file

Tapi sayangnya, ada bug di ksh93 (setidaknya versi saya (93u +)), dalam hal file tersebut tampaknya terpotong hingga panjang nol dalam kasus itu.

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

Tampaknya mengatasi bug itu, tapi sekarang, itu jauh lebih berbelit-belit daripada perintah sed.


Harap gabungkan jawaban Anda menjadi satu entri yang diformat dengan baik dengan panduan cepat untuk kapan setiap solusi harus digunakan. Berbagai pendekatan untuk masalah yang berbeda-beda yang semuanya dicampuradukkan dalam jawaban mengambang telah membuat pertanyaan ini sedikit menjadi bencana untuk dibaca.
Caleb

@ Caleb, Semuanya bermuara pada pertanyaan yang sangat tidak jelas, jadi semua jawaban semua orang adalah untuk interpretasi yang berbeda dari pertanyaan itu. Untuk setiap jawaban, saya mencoba mengatakan pertanyaan mana yang ingin dijawab.
Stéphane Chazelas

Hanya FYI: Mencoba awk '/./' file 1<>; fileyang berhasil. Bagi saya, itu bahkan lebih jelas daripadased '/./!d'
grebneke

5

Berikut ini Perlsatu kalimat untuknya:

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

EDIT: Peningkatan kode berdasarkan komentar ruakh di bawah ini.


1
Atauperl -ni -e '/./ and print' yourfile
derobert

1
@peterph $adalah jangkar (yaitu nol-lebar) sehingga tidak termasuk baris baru. Mengenai ruang yang berlebihan, itu alasan saya menambahkan /xsaya tidak ingin Perlmencoba memasukkan `$ \` ke dalam regex
Joseph R.

1
Anda tidak perlu $, mengingat bahwa Anda memiliki \n. (Atau - Anda tidak perlu \n, mengingat bahwa Anda memiliki \s*dan $; tetapi saya pikir s/^\s*\n//membuatnya lebih jelas bahwa baris baru dihapus.) Anda juga tidak perlu /m; itu tidak berpengaruh pada perintah ini. Dan begitu Anda menyingkirkan $dan ruang, Anda tidak perlu /x.
ruakh

1
@ JosephR.: Itu \nsendiri dapat dihapus; apa yang tidak dapat Anda lakukan adalah menghapus baik yang $ dan yang \n. Jadi s/^\s*//akan memiliki masalah yang Anda gambarkan, tetapi s/^\s*$//akan baik-baik saja, karena \s*dan $. (Apakah Anda mengerti maksud saya?)
ruakh

1
@ JosephR .: Apa yang terjadi adalah, $ dapat cocok sebelum baris baru (asalkan /mbendera diaktifkan, atau baris baru adalah karakter terakhir dari string, atau keduanya), tetapi juga dapat cocok dengan akhir string. Sebagai contoh, "abc" =~ m/^abc$/itu benar. Dalam kasus \s*$, \s*cukup serakah untuk memakan baris baru, dan kemudian $cocok dengan akhir-string. (Tapi saya pikir s/^\s*\n//itu lebih jelas, jadi jawaban Anda baik-baik saja seperti sekarang.)
ruakh

5

Berdasarkan klarifikasi dalam komentar untuk pertanyaan Anda, sesuatu seperti:

awk -v RS= -v ORS= 1

dapat melakukan apa yang Anda inginkan.

Pemisah rekaman kosong adalah kasus khusus yang memberi tahu awkbahwa catatan harus berupa paragraf (dipisahkan oleh urutan baris kosong). Mengatur pemisah catatan output ke string kosong juga berarti bahwa konten paragraf (tanpa pemisah) harus digabungkan. 1hanyalah kondisi yang benar untuk mencetak setiap catatan.

Namun itu akan menghilangkan baris baru, sehingga Anda dapat melakukan:

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

3

Saya tahu ini akan lebih mudah jika saya memberikan file, tetapi sayangnya itu berisi informasi rahasia yang tidak bisa saya bagikan. Sementara itu, saya menulis naskah ruby ​​yang sepertinya berhasil:

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

Terima kasih semuanya telah membantu!


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

menghasilkan

"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

Saya menemukan ide untuk solusi yang mungkin pada stackoverflow .

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

Anda mungkin harus membuat cadangan file csv Anda sebelum mengujinya, tetapi setidaknya untuk contoh yang Anda berikan itu berfungsi dengan sempurna.

Penjelasan yang baik tentang cara kerja bagian dalam ekspresi ini ditawarkan pada jawabannya, saya hanya mengeditnya untuk mencari baris yang tidak diakhiri dengan a "( [^"]\n).


1

Jika, dari respons Anda sendiri, Anda ingin menghapus karakter baris baru yang terkandung di dalam string yang dikutip, Anda bisa melakukan:

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

Anda juga bisa menggunakan -iflag perl menggunakan untuk mengedit file di tempat .

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

Atau dengan GNU awk:

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

atau:

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

(jika Anda bersaing untuk yang terpendek)

Perhatikan bahwa mereka menganggap bahwa tidak ada karakter kutip ganda yang lolos dalam input.


0

Sepertinya efeknya yang Anda inginkan lebih dari menghapus baris kosong, tetapi menghapus setiap urutan 2 atau lebih karakter baris baru.

Yang bisa Anda lakukan dengan perl:

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

Anda juga bisa menggunakan -iflag perl menggunakan untuk mengedit file di tempat .

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

0

Ada cara yang lebih singkat untuk menghapus garis kosong di AWK:

awk 'NF' file

Tetapi untuk mendapatkan output yang Anda inginkan, yang dibutuhkan hanyalah satu liner sederhana:

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

Penjelasan

Dalam AWK, baris kosong berarti baris / catatan tidak memiliki bidang, yaitu NFvariabel (Jumlah Bidang) adalah nol. Satu liner di atas hanya akan mengeksekusi ketika NF > 0, mencetak semua baris, tetapi yang kosong.

Ini i++adalah penghitung garis yang tidak kosong.

Ini !(i % 2)digunakan untuk mencetak dua baris non-kosong berturut-turut dengan cara yang Anda inginkan, yaitu, setiap kali kelipatan 2 ditemukan, modulopernyataan !(i % 2)menghasilkan 1, yang mengakhiri rangkaian dua baris non-kosong.


Salahku! Maaf. Saya tidak membaca seluruh pertanyaannya dan hasil yang diinginkan. Jawabannya sudah diperbaiki sekarang. Terima kasih. :-)
Marcelo Augusto

0

Anda dapat menggunakan Vim dalam mode Ex:

ex -sc v/./d -cx b.csv
  1. v/./ temukan garis kosong

  2. d menghapus

  3. x Simpan dan tutup

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.