it-swarm-id.com

Bagaimana cara menghitung jumlah karakter tertentu di setiap baris?

Saya bertanya-tanya bagaimana cara menghitung jumlah karakter tertentu di setiap baris dengan beberapa utilitas pemrosesan teks?

Misalnya, untuk menghitung " di setiap baris teks berikut

"hello!" 
Thank you!

Baris pertama memiliki dua, dan baris kedua memiliki 0.

Contoh lain adalah menghitung ( di setiap baris.

97
Tim

Anda dapat melakukannya dengan sed dan awk:

$ sed 's/[^"]//g' dat | awk '{ print length }'
2
0

Di mana dat adalah teks contoh Anda, sed menghapus (untuk setiap baris) semua karakter non - " Dan awk mencetak untuk setiap baris ukurannya (yaitu length setara dengan length($0), di mana $0 menunjukkan baris saat ini).

Untuk karakter lain, Anda hanya perlu mengubah ekspresi sed. Misalnya untuk ( Ke:

's/[^(]//g'

Pembaruan:sed agak berlebihan untuk tugas - tr sudah cukup. Solusi yang setara dengan tr adalah:

$ tr -d -c '"\n' < dat | awk '{ print length; }'

Artinya tr menghapus semua karakter yang bukan (-c Berarti pelengkap) dalam rangkaian karakter "\n.

115
maxschlepzig

Saya hanya akan menggunakan awk

awk -F\" '{print NF-1}' <fileName>

Di sini kita mengatur pemisah bidang (dengan bendera -F) menjadi karakter " maka yang kita lakukan hanyalah mencetak jumlah bidang NF - 1. Jumlah kemunculan karakter target akan lebih sedikit dari jumlah bidang yang dipisahkan.

Untuk karakter lucu yang ditafsirkan oleh Shell, Anda hanya perlu memastikan bahwa Anda menghindarinya jika tidak, baris perintah akan mencoba dan menafsirkannya. Jadi untuk keduanya " dan ) Anda harus keluar dari pemisah bidang (dengan \).

52
Martin York

Menggunakan tr ard wc:

function countchar()
{
    while IFS= read -r i; do printf "%s" "$i" | tr -dc "$1" | wc -m; done
}

Pemakaian:

$ countchar '"' <file.txt  #returns one count per line of file.txt
1
3
0

$ countchar ')'           #will count parenthesis from stdin
$ countchar '0123456789'  #will count numbers from stdin
15

Namun implementasi lain yang tidak bergantung pada program eksternal, dalam bash, zsh, yash dan beberapa implementasi/versi ksh:

while IFS= read -r line; do 
  line="${line//[!\"]/}"
  echo "${#line}"
done <input-file

Gunakan line="${line//[!(]}"untuk menghitung (.

11
enzotib

Jawaban menggunakan awk gagal jika jumlah kecocokan terlalu besar (yang merupakan situasi saya). Untuk jawaban dari loki-astari , kesalahan berikut dilaporkan:

awk -F" '{print NF-1}' foo.txt 
awk: program limit exceeded: maximum number of fields size=32767
    FILENAME="foo.txt" FNR=1 NR=1

Untuk jawaban dari enzotib (dan setara dengan manatwork ), kesalahan segmentasi terjadi:

awk '{ gsub("[^\"]", ""); print length }' foo.txt
Segmentation fault

Solusi sed oleh maxschlepzig bekerja dengan benar, tetapi lambat (timing di bawah).

Beberapa solusi belum disarankan di sini. Pertama, menggunakan grep:

grep -o \" foo.txt | wc -w

Dan menggunakan Perl:

Perl -ne '$x+=s/\"//g; END {print "$x\n"}' foo.txt

Berikut adalah beberapa timing untuk beberapa solusi (dipesan paling lambat hingga tercepat); Saya membatasi beberapa hal di sini. 'foo.txt' adalah file dengan satu baris dan satu string panjang yang berisi 84922 kecocokan.

## sed solution by [maxschlepzig]
$ time sed 's/[^"]//g' foo.txt | awk '{ print length }'
84922
real    0m1.207s
user    0m1.192s
sys     0m0.008s

## using grep
$ time grep -o \" foo.txt | wc -w
84922
real    0m0.109s
user    0m0.100s
sys     0m0.012s

## using Perl
$ time Perl -ne '$x+=s/\"//g; END {print "$x\n"}' foo.txt
84922
real    0m0.034s
user    0m0.028s
sys     0m0.004s

## the winner: updated tr solution by [maxschlepzig]
$ time tr -d -c '\"\n' < foo.txt |  awk '{ print length }'
84922
real    0m0.016s
user    0m0.012s
sys     0m0.004s
10
josephwb

Solusi awk lain:

awk '{print gsub(/"/, "")}'
9

Kemungkinan implementasi lain dengan awk dan gsub:

awk '{ gsub("[^\"]", ""); print length }' input-file

Fungsi gsub adalah setara dengan sed 's///g'.

Gunakan gsub("[^(]", "") untuk menghitung (.

8
enzotib

Saya memutuskan untuk menulis program C karena saya bosan.

Anda mungkin harus menambahkan validasi input, tetapi selain itu sudah diatur.

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[])
{
        char c = argv[1][0];
        char * line = NULL;
        size_t len = 0;
        while (getline(&line, &len, stdin) != -1)
        {
                int count = 0;
                char * s = line;
                while (*s) if(*s++ == c) count++;
                printf("%d\n",count);
        }
        if(line) free(line);
}
6
user606723

Untuk string, yang paling sederhana adalah dengan tr dan wc (tidak perlu berlebihan dengan awk atau sed) - tetapi perhatikan komentar di atas tentang tr, menghitung byte, bukan karakter -

echo $x | tr -d -c '"' | wc -m

dimana $x adalah variabel yang berisi string (bukan file) untuk dievaluasi.

6
Ocumo

Berikut ini adalah solusi C lain yang hanya membutuhkan STD C dan lebih sedikit memori:

#include <stdio.h>

int main(int argc, char **argv)
{
  if (argc < 2 || !*argv[1]) {
    puts("Argument missing.");
    return 1;
  }
  char c = *argv[1], x = 0;
  size_t count = 0;
  while ((x = getc(stdin)) != EOF)
    if (x == '\n') {
      printf("%zd\n", count);
      count = 0;
    } else if (x == c)
      ++count;
  return 0;
}
4
maxschlepzig

Mungkin jawaban yang lebih langsung, jawaban yang benar-benar awk adalah menggunakan split. Split mengambil string dan mengubahnya menjadi array, nilai kembalinya adalah jumlah item array yang dihasilkan +1.

Kode berikut akan mencetak berapa kali "muncul di setiap baris.

awk ' {print (split($0,a,"\"")-1) }' file_to_parse

info lebih lanjut tentang split http://www.staff.science.uu.nl/~oostr102/docs/nawk/nawk_92.html

3
bleurp

Kita dapat menggunakan grep dengan regex untuk membuatnya lebih sederhana dan kuat.

Untuk menghitung karakter tertentu.

$ grep -o '"' file.txt|wc -l

Untuk menghitung karakter khusus termasuk karakter spasi.

$ grep -Po '[\W_]' file.txt|wc -l

Di sini kita memilih karakter apa saja dengan [\S\s] dan dengan -o opsi yang kami buat grep untuk mencetak setiap kecocokan (yaitu, masing-masing karakter) dalam baris terpisah. Dan kemudian gunakan wc -l untuk menghitung setiap baris.

3
Kannan Mohan

Untuk solusi bash murni (namun, ini spesifik untuk bash): If $x adalah variabel yang berisi string Anda:

x2="${x//[^\"]/}"
echo ${#x2}

${x// hal menghapus semua karakter kecuali ", ${#x2} menghitung panjang istirahat ini.

(Saran asli menggunakan expr yang memiliki masalah, lihat komentar:)

expr length "${x//[^\"]/}"
2
Marian

Berikut ini adalah skrip Python sederhana untuk menemukan hitungan " di setiap baris file:

#!/usr/bin/env python2
with open('file.txt') as f:
    for line in f:
        print line.count('"')

Di sini kita telah menggunakan metode count untuk tipe str built-in.

2
heemayl

Ganti a oleh karakter yang akan dihitung. Output adalah penghitung untuk setiap baris.

Perl -nE 'say y!a!!'
2
JJoao

Perbandingan waktu dari solusi yang disajikan (bukan jawaban)

Efisiensi jawaban tidak penting. Namun demikian, mengikuti pendekatan @josephwb, saya mencoba mengatur waktu semua jawaban yang disajikan.

Saya menggunakan sebagai input terjemahan bahasa Portugis dari Victor Hugo "Les Miserables" (buku hebat!) Dan menghitung kemunculan "a". Edisi saya memiliki 5 volume, banyak halaman ...

$ wc miseraveis.txt 
29331  304166 1852674 miseraveis.txt 

Jawaban C dikompilasi dengan gcc, (tanpa optimisasi).

Setiap jawaban dijalankan 3 kali dan pilih yang terbaik.

Jangan terlalu mempercayai angka-angka ini (mesin saya melakukan tugas-tugas lain, dll, dll.). Saya berbagi waktu ini dengan Anda, karena saya mendapat beberapa hasil yang tidak terduga dan saya yakin Anda akan menemukan lebih banyak lagi ...

  • 14 dari 16 solusi waktunya kurang dari 1s; 9 kurang dari 0,1, banyak dari mereka menggunakan pipa
  • 2 solusi, menggunakan bash baris demi baris, memproses baris 30k dengan menciptakan proses baru, menghitung solusi yang tepat dalam 10 detik/20 detik.
  • grep -oP a adalah kali pohon lebih cepat dari itu grep -o a (10; 11 vs 12)
  • Perbedaan antara C dan yang lainnya tidak begitu besar seperti yang saya harapkan. (7; 8 vs 2; 3)
  • (kesimpulan diterima)

(hasil dalam urutan acak)

=========================1 maxschlepzig
$ time sed 's/[^a]//g' mis.txt | awk '{print length}' > a2
real    0m0.704s ; user 0m0.716s
=========================2 maxschlepzig
$ time tr -d -c 'a\n' < mis.txt | awk '{ print length; }' > a12
real    0m0.022s ; user 0m0.028s
=========================3 jjoao
$ time Perl -nE 'say y!a!!' mis.txt  > a1
real    0m0.032s ; user 0m0.028s
=========================4 Stéphane Gimenez
$ function countchar(){while read -r i; do echo "$i"|tr -dc "$1"|wc -c; done }

$ time countchar "a"  < mis.txt > a3
real    0m27.990s ; user    0m3.132s
=========================5 Loki Astari
$ time awk -Fa '{print NF-1}' mis.txt > a4
real    0m0.064s ; user 0m0.060s
Error : several -1
=========================6 enzotib
$ time awk '{ gsub("[^a]", ""); print length }' mis.txt > a5
real    0m0.781s ; user 0m0.780s
=========================7 user606723
#include <stdio.h> #include <string.h> // int main(int argc, char *argv[]) ...  if(line) free(line); }

$ time a.out a < mis.txt > a6
real    0m0.024s ; user 0m0.020s
=========================8 maxschlepzig
#include <stdio.h> // int main(int argc, char **argv){if (argc < 2 || !*argv[1]) { ...  return 0; }

$ time a.out a < mis.txt > a7
real    0m0.028s ; user 0m0.024s
=========================9 Stéphane Chazelas
$ time awk '{print gsub(/a/, "")}'< mis.txt > a8
real    0m0.053s ; user 0m0.048s
=========================10 josephwb count total
$ time grep -o a < mis.txt | wc -w > a9
real    0m0.131s ; user 0m0.148s
=========================11 Kannan Mohan count total
$ time grep -o 'a' mis.txt | wc -l > a15
real    0m0.128s ; user 0m0.124s
=========================12 Kannan Mohan count total
$ time grep -oP 'a' mis.txt | wc -l > a16
real    0m0.047s ; user 0m0.044s
=========================13 josephwb Count total
$ time Perl -ne '$x+=s/a//g; END {print "$x\n"}'< mis.txt > a10
real    0m0.051s ; user 0m0.048s
=========================14 heemayl
#!/usr/bin/env python2 // with open('mis.txt') as f: for line in f: print line.count('"')

$ time pyt > a11
real    0m0.052s ; user 0m0.052s
=========================15 enzotib
$ time  while IFS= read -r line; do   line="${line//[!a]/}"; echo "${#line}"; done < mis.txt  > a13
real    0m9.254s ; user 0m8.724s
=========================16 bleurp
$ time awk ' {print (split($0,a,"a")-1) }' mis.txt > a14
real    0m0.148s ; user 0m0.144s
Error several -1
2
JJoao
grep -n -o \" file | sort -n | uniq -c | cut -d : -f 1

di mana grep melakukan semua pengangkatan berat: melaporkan setiap karakter yang ditemukan di setiap nomor baris. Sisanya hanya untuk menjumlahkan jumlah per baris, dan memformat output.

Hapus -n dan dapatkan jumlah untuk seluruh file.

Menghitung file teks 1,5Meg di bawah 0,015 detik tampaknya cepat.
Dan berfungsi dengan karakter (bukan byte).

1
user79743

Solusi untuk bash. Tidak ada program eksternal yang disebut (lebih cepat untuk string pendek).

Jika nilainya dalam variabel:

$ a='"Hello!"'

Ini akan mencetak berapa banyak " itu mengandung:

$ b="${a//[^\"]}"; echo "${#b}"
2
1
Isaac