Menggunakan Perl untuk menghitung jumlah angka ilmiah dalam file

10

Bagaimana saya bisa menghitung jumlah angka ilmiah dalam suatu file? File ini juga memiliki beberapa baris header yang perlu dilewati.

Sebagian konten file ada di bawah.

FileHeaderLine1
FileHeaderLine2
FileHeaderLine3
FileHeaderLine4
2.91999996E-001 2.97030300E-001 3.02060604E-001 3.07090908E-001 3.12121212E-001 3.17151517E-001
3.22181821E-001 3.27212125E-001 3.32242429E-001 3.37272733E-001 3.42303038E-001 3.47333342E-001
3.52363646E-001 3.57393950E-001 3.62424254E-001 3.67454559E-001 3.72484863E-001 3.77515137E-001
3.82545441E-001 3.87575746E-001 3.92606050E-001 3.97636354E-001 4.02666658E-001 4.07696962E-001
4.12727267E-001 4.17757571E-001 4.22787875E-001 4.27818179E-001 4.32848483E-001 4.37878788E-001
4.42909092E-001 4.47939396E-001 4.52969700E-001

Jadi, bagaimana saya bisa melewati empat baris pertama dari contoh di atas dan menghitung jumlah angka ilmiah dalam file?

AFP
sumber

Jawaban:

14

Dengan modul inti Scalar::Util, Anda dapat melakukan:

$ perl -MScalar::Util=looks_like_number -anle '
    $count += grep { looks_like_number($_) } @F;
    END { print $count }
' file
33

Lebih lanjut tentang looks_like_numberdapat melihat perldoc perlapi.

cuonglm
sumber
+1 keren, saya tidak tahulooks_like_number
steeldriver
7

Menggunakan GNU grep

Anda dapat menggunakannya grepuntuk melakukan ini, menggunakan fasilitas PCRE. Kebetulan pola yang sama dapat digunakan di Perl juga:

$ grep -oP '\d+E[-+]?\d+' file.txt  | wc -l
33

Anda juga dapat menggunakan wc -wuntuk menghitung kata-kata, saya menghitung baris di atas, tetapi grepmengembalikan satu kecocokan pada sebuah baris sehingga tidak terlalu penting dalam skenario itu.

Menggunakan Perl

Untuk Perl Anda bisa menggunakan liner yang satu ini:

$ perl -lane '$c += grep /\d+E[-+]?\d+/, @F; END { print $c; }' file.txt 
33

Referensi

slm
sumber
@StephaneChazelas - terima kasih untuk hasil editnya. Maaf saya hanya ada di sistem GNU jadi cenderung lupa tentang hal ini sepanjang waktu. Saya akan mencoba untuk tidak membuat kesalahan itu.
slm
4

egrep akan bekerja:

egrep "[0-9].[0-9]E-[0-9]" YourFile | wc -w

MEMPERBARUI:

jika suatu baris mengandung nomor dan beberapa string lainnya, kita dapat menggunakan awkuntuk menyelesaikan masalah:

awk -F' ' '{for(i=1;i<=NF;i++)if(!(i%1))$i=$i "\n"}1' YourFile | egrep "[0-9].[0-9]E-[0-9]" | wc -w ( or wc -l )
Nidal
sumber
Ini akan memberikan hasil yang salah jika suatu baris mengandung nomor dan beberapa string lainnya. Jawaban di atas yang menggunakan opsi-o grep untuk hanya menampilkan kecocokan lebih benar.
Johnny
Saya tidak tahu tentang -oPopsi yang disebutkan dalam jawaban slm sebelumnya, tapi saya telah memperbaiki masalah saya menggunakan awk@ Johnny
Nidal
3

Dengan asumsi Anda hanya memiliki angka ilmiah setelah baris ke-4, Anda dapat melakukan sesuatu seperti di bawah ini.

tail -n +5 filename | wc - w

Untuk input yang Anda berikan, outputnya adalah 33 setelah menjalankan perintah di atas.

Ramesh
sumber
3

Jika Anda hanya perlu menghitung jumlah bidang dibatasi spasi putih mengikuti baris header di perl, saya pikir Anda bisa melakukan

perl -lane '$sum += $#F+1 if $. > 4; END{print $sum}' file

Jika Anda benar-benar perlu menghitung hanya angka yang diformat secara ilmiah maka satu pendekatan mungkin untuk mencari dan mengganti angka sesuai dengan regex yang sesuai dan kemudian menghitung jumlah penggantian (ekspresi substitusi perl mengembalikan jumlah penggantian ketika Anda mengikatnya ke variabel )

perl -lane '$sum += s/[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?//g if $. > 4; END{print $sum}' file
Steeldriver
sumber
2

Semuanya berujung pada apa yang sebenarnya ingin Anda pertimbangkan sebagai angka ilmiah , apa yang bisa Anda harapkan dari input Anda, dan di mana Anda dapat menerima untuk menemukan angka-angka itu di input.

Misalnya, di:

That's inferior to the LK2E2000 model.

Saya dapat menemukan angka 0 atau 2 (inf dan 2E2000) atau 3 (inf, 2E200, 0) (atau diambil secara ekstrem, mencari semua urutan karakter yang membentuk angka yang valid: 17 (inf, 2, 2E2, 2E20, 2E200, 2E200, 2E2000, 2, 20, 200, 2000, 0, 00, 000, 0, 00, 0)).

Jika Anda tahu input Anda hanya memiliki angka di X.XXXXXXXXE-XXX, dan bahwa mereka menggunakan kata-kata sendiri, mungkin lebih aman untuk mencari hanya dengan seluruh kata seperti:

tr -s '[[:blank:]]' '[\n*]' | LC_ALL=C grep -xEc '[0-9]\.[0-9]{8}E-[0-9]{3}'

Idenya di sana, adalah untuk mendapatkan satu kata per baris dan untuk mencocokkan seluruh baris ( -x) dengan pola yang Anda inginkan. Untuk mengizinkan nomor notifikasi ilmiah (-1.2e + 1234 ... selama ada eatau E), Anda dapat mengubah pola menjadi:

[-+]?([0-9]+\.[0-9]*|[0-9]*\.[0-9])[eE][-+]?[0-9]+

Atau jadikan e...bagian itu opsional untuk memungkinkan semua jenis angka floating point desimal:

[-+]?([0-9]+\.[0-9]*|[0-9]*\.[0-9])([eE][-+]?[0-9]+)?

Itu semua memberikan jawaban yang sama untuk input spesifik Anda, tetapi di mana yang akan membuat perbedaan adalah di mana ada input yang berangkat dari pola ketat yang ditunjukkan dalam sampel Anda.

Stéphane Chazelas
sumber