Saya memiliki file input yang dibatasi dengan koma ( ,
). Ada beberapa bidang terlampir dalam tanda kutip ganda yang memiliki koma di dalamnya. Berikut adalah contoh baris
123,"ABC, DEV 23",345,534.202,NAME
Saya perlu menghapus semua koma yang terjadi di dalam tanda kutip ganda dan tanda kutip ganda juga. Jadi baris di atas harus diurai menjadi seperti yang ditunjukkan di bawah ini
123,ABC DEV 23,345,534.202,NAME
Saya mencoba yang berikut ini sed
tetapi tidak memberikan hasil yang diharapkan.
sed -e 's/\(".*\),\(".*\)/\1 \2/g'
Adakah trik cepat dengan sed
, awk
atau utilitas unix lainnya?
text-processing
sed
awk
csv
mtk
sumber
sumber
Jawaban:
Jika kuotasi seimbang, Anda ingin menghapus koma di antara setiap kuotasi lainnya, ini dapat dinyatakan dalam
awk
seperti ini:Keluaran:
Penjelasan
Tanda
-F"
awk memisahkan garis pada tanda kutip ganda, yang berarti setiap bidang lainnya akan menjadi teks antar kutip. Menjalankan for-loopgsub
, kependekan dari pengganti global, pada setiap bidang lainnya, menggantikan koma (","
) dengan tidak ada (""
). The1
pada akhir memanggil default kode-blok:{ print $0 }
.sumber
gsub
dan menjelaskan secara singkat, bagaimana liner yang satu ini bekerja ?? silahkan.{ print $0 }
. Saya menambahkan itu ke penjelasannya juga.prefix,"something,otherthing[newline]something , else[newline]3rdline,and,things",suffix
(yaitu: beberapa baris, dan bersarang "," di mana saja dalam tanda kutip ganda multi-baris: seluruh"...."
bagian harus bergabung kembali dan di dalam,
harus diganti / dihapus ...): skrip Anda tidak akan melihat pasangan tanda kutip ganda dalam kasus itu, dan itu tidak benar-benar mudah untuk dipecahkan (perlu "bergabung kembali" dengan garis yang ada di "terbuka" (yaitu, ganjil bernomor) kutipan ganda ... + hati-hati jika ada juga yang lolos\"
di dalam string)awk -F'"' -v OFS='"' '{ for (I=1; i<=NF; i+=2) gsub(",", "|", $i) } 1' infile
Ada respons yang baik , menggunakan sed hanya satu kali dengan satu loop :
Penjelasan:
:a;
adalah label untuk cabang furters/^\(\([^"]*,\?\|"[^",]*",\?\)*"[^",]*\),/\1 /
dapat berisi 3 bagian tertutup[^"]*,\?\|"[^",]*",\?
cocok dengan string yang tidak mengandung kuotasi ganda, mungkin diikuti oleh koma atau string yang dilingkupi oleh dua kuotasi ganda, tanpa koma dan mungkin diikuti oleh koma.ta
akan diulang:a
jikas/
perintah sebelumnya melakukan beberapa perubahan.sumber
Solusi umum yang juga dapat menangani beberapa koma di antara kuotasi seimbang membutuhkan substitusi bersarang. Saya menerapkan solusi dalam perl, yang memproses setiap baris dari input yang diberikan dan hanya koma pengganti di setiap pasangan kutipan lainnya:
atau singkatnya
Anda bisa mem-pipe teks yang ingin Anda proses ke perintah atau menentukan file teks untuk diproses sebagai argumen baris perintah terakhir.
sumber
[^\\]
akan memiliki efek yang tidak diinginkan dari pencocokan karakter terakhir dalam tanda kutip dan menghapus (non \ karakter), yaitu, Anda tidak harus mengkonsumsi karakter itu. Coba(?<!\\)
saja.[^"]*
untuk membuat pertandingan non-serakah (yaitu cocok segala sesuatu dari satu"
ke berikutnya"
):perl -pe 's/"([^"]+)"/($match = $1) =~ (s:,::g);$match;/ge;'
. Itu tidak mengakui ide aneh bahwa kutipan mungkin lolos dengan backslash :-)[^"]*
pendekatan atau pendekatan non-serakah eksplisit mengkonsumsi waktu cpu lebih sedikit.Saya akan menggunakan bahasa dengan parser CSV yang tepat. Sebagai contoh:
sumber
Kutipan kedua Anda salah tempat:
Selain itu, menggunakan ekspresi reguler cenderung cocok dengan bagian teks terpanjang yang mungkin, artinya ini tidak akan berfungsi jika Anda memiliki lebih dari satu bidang yang dikutip dalam string.
Cara yang menangani beberapa bidang dikutip dalam sed
Namun, ini juga merupakan cara untuk menyelesaikannya, dengan input yang mungkin mengandung lebih dari satu koma per bidang yang dikutip, ekspresi pertama dalam sed harus diulangi sebanyak yang koma maksimum dalam satu bidang, atau sampai tidak mengubah output sama sekali.
Menjalankan sed dengan lebih dari satu ekspresi harus lebih efisien daripada beberapa proses sed berjalan dan "tr" semua berjalan dengan pipa terbuka.
Namun, ini mungkin memiliki konsekuensi yang tidak diinginkan jika input tidak diformat dengan benar. yaitu kutipan bersarang, kutipan tidak ditentukan.
Menggunakan contoh yang berjalan:
Keluaran:
sumber
sed -r ':r; s/("[^",]+),([^",]*)/\1 \2/g; tr; s/"//g'
.Dalam perl - Anda dapat menggunakannya
Text::CSV
untuk menguraikan ini, dan melakukannya dengan sepele:Anda dapat mencetak dengan
Text::CSV
tetapi cenderung mempertahankan tanda kutip jika Anda melakukannya. (Meskipun, saya sarankan - daripada mencabut tanda kutip untuk output Anda, Anda bisa mem-parsing menggunakanText::CSV
di tempat pertama).sumber
Saya membuat fungsi untuk loop melalui setiap karakter dalam string.
Jika karakter adalah kutipan maka tanda centang (b_in_qt) ditandai benar.
Meskipun b_in_qt benar, semua koma diganti dengan spasi.
b_in_qt disetel ke false ketika koma berikutnya ditemukan.
sumber