it-swarm-vi.com

Làm thế nào để đếm số lượng của một nhân vật cụ thể trong mỗi dòng?

Tôi đã tự hỏi làm thế nào để đếm số lượng một ký tự cụ thể trong mỗi dòng bằng một số tiện ích xử lý văn bản?

Ví dụ: để đếm " trong mỗi dòng của văn bản sau

"hello!" 
Thank you!

Dòng đầu tiên có hai và dòng thứ hai có 0.

Một ví dụ khác là đếm ( trong mỗi dòng.

97
Tim

Bạn có thể làm điều đó với sedawk:

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

Trong đó dat là văn bản mẫu của bạn, sed xóa (cho mỗi dòng) tất cả các ký tự không - "awk in cho mỗi dòng kích thước của nó (tức là length tương đương với length($0), trong đó $0 biểu thị dòng hiện tại).

Đối với một nhân vật khác, bạn chỉ cần thay đổi biểu thức sed. Ví dụ: ( Đến:

's/[^(]//g'

Cập nhật :sed là loại quá mức cần thiết cho tác vụ - tr là đủ. Một giải pháp tương đương với tr là:

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

Có nghĩa là tr xóa tất cả các ký tự không (-c Có nghĩa là bổ sung) trong bộ ký tự "\n.

115
maxschlepzig

Tôi sẽ chỉ sử dụng awk

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

Ở đây chúng tôi đặt dấu tách trường (với cờ -F) là ký tự " thì tất cả những gì chúng ta làm là in số lượng trường NF - 1. Số lần xuất hiện của ký tự đích sẽ ít hơn một số trường được tách.

Đối với các nhân vật ngộ nghĩnh được Shell giải thích, bạn chỉ cần đảm bảo rằng bạn thoát khỏi chúng nếu không dòng lệnh sẽ thử và giải thích chúng. Vì vậy, cho cả hai ") bạn cần thoát khỏi dấu phân cách trường (với \).

52
Martin York

Sử dụng tr ard wc:

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

Sử dụng:

$ 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
Stéphane Gimenez

Tuy nhiên, một triển khai khác không dựa vào các chương trình bên ngoài, trong bash, zsh, yash và một số triển khai/phiên bản của ksh:

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

Sử dụng line="${line//[!(]}"để đếm (.

11
enzotib

Các câu trả lời sử dụng awk không thành công nếu số lượng kết quả quá lớn (điều này xảy ra là tình huống của tôi). Đối với câu trả lời từ loki-astari , lỗi sau đây được báo cáo:

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

Đối với câu trả lời từ enzotib (và tương đương từ manatwork ), một lỗi phân đoạn xảy ra:

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

Giải pháp sed của maxschlepzig hoạt động chính xác, nhưng chậm (thời gian bên dưới).

Một số giải pháp chưa được đề xuất ở đây. Đầu tiên, sử dụng grep:

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

Và sử dụng Perl:

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

Dưới đây là một số thời gian cho một vài giải pháp (được đặt hàng từ chậm nhất đến nhanh nhất); Tôi giới hạn mọi thứ cho một lớp lót ở đây. 'foo.txt' là một tệp có một dòng và một chuỗi dài chứa 84922 kết quả khớp.

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

Một giải pháp awk khác:

awk '{print gsub(/"/, "")}'
9
Stéphane Chazelas

Một cách thực hiện khác có thể với awk và gsub:

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

Hàm gsub tương đương với sed 's///g'.

Sử dụng gsub("[^(]", "") để đếm (.

8
enzotib

Tôi quyết định viết một chương trình C vì tôi đã chán.

Có lẽ bạn nên thêm xác thực đầu vào, nhưng khác với tất cả đã được đặt.

#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

Đối với một chuỗi, đơn giản nhất sẽ là với trwc (không cần quá mức cần thiết với awk hoặc sed) - nhưng lưu ý các nhận xét trên về tr, đếm byte, không phải ký tự -

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

ở đâu $x là biến chứa chuỗi (không phải tệp) để đánh giá.

6
Ocumo

Đây là một giải pháp C khác chỉ cần STD C và ít bộ nhớ hơn:

#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

Có lẽ một câu trả lời thẳng thắn hơn, hoàn toàn awk sẽ là sử dụng chia. Split lấy một chuỗi và biến nó thành một mảng, giá trị trả về là số lượng các mục mảng được tạo + 1.

Đoạn mã sau sẽ in ra số lần "xuất hiện trên mỗi dòng.

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

thêm thông tin về sự phân chia http://www.staff.science.uu.nl/~oostr102/docs/nawk/nawk_92.html

3
bleurp

Chúng ta có thể sử dụng grep với regex để làm cho nó đơn giản và mạnh mẽ hơn.

Để tính nhân vật cụ thể.

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

Để đếm các ký tự đặc biệt bao gồm các ký tự khoảng trắng.

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

Ở đây chúng tôi đang chọn bất kỳ nhân vật nào có [\S\s] và với -o tùy chọn chúng tôi thực hiện grep để in từng trận đấu (nghĩa là, mỗi ký tự) trong một dòng riêng biệt. Và sau đó sử dụng wc -l để đếm từng dòng.

3
Kannan Mohan

Đối với giải pháp bash thuần túy (tuy nhiên, đó là bash cụ thể): If $x là biến chứa chuỗi của bạn:

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

Các ${x// điều loại bỏ tất cả các ký tự trừ ", ${#x2} tính toán độ dài của phần còn lại này.

(Đề xuất ban đầu sử dụng expr có vấn đề, xem bình luận :)

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

Đây là một tập lệnh Python đơn giản để tìm số " trong mỗi dòng của một tệp:

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

Ở đây chúng tôi đã sử dụng phương thức count của loại str tích hợp.

2
heemayl

Thay thế a bằng char sẽ được tính. Đầu ra là bộ đếm cho mỗi dòng.

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

So sánh thời gian của các giải pháp được trình bày (không phải là câu trả lời)

Hiệu quả của các câu trả lời là không quan trọng. Tuy nhiên, theo cách tiếp cận @josephwb, tôi đã cố gắng tính thời gian cho tất cả các câu trả lời được trình bày.

Tôi sử dụng làm đầu vào bản dịch tiếng Bồ Đào Nha của Victor Hugo "Les Miserables" (cuốn sách tuyệt vời!) Và đếm số lần xuất hiện của "a". Phiên bản của tôi có 5 tập, nhiều trang ...

$ wc miseraveis.txt 
29331  304166 1852674 miseraveis.txt 

Câu trả lời C được biên dịch với gcc, (không tối ưu hóa).

Mỗi câu trả lời được chạy 3 lần và chọn câu trả lời hay nhất.

Đừng tin tưởng quá nhiều vào những con số này (máy của tôi đang thực hiện các tác vụ khác, v.v.). Tôi chia sẻ những lúc này với bạn, vì tôi nhận được một số kết quả bất ngờ và tôi chắc chắn bạn sẽ tìm thấy thêm ...

  • 14 trong số 16 giải pháp tính thời gian mất ít hơn 1 giây; 9 ít hơn 0,1 giây, nhiều người trong số họ sử dụng đường ống
  • 2 giải pháp, sử dụng bash line theo dòng, xử lý các dòng 30k bằng cách tạo các quy trình mới, tính toán giải pháp chính xác trong 10 giây/20 giây.
  • grep -oP a là lần cây nhanh hơn grep -o a (10; 11 so với 12)
  • Sự khác biệt giữa C và những người khác không quá lớn như tôi mong đợi. (7; 8 so với 2; 3)
  • (kết luận hoan nghênh)

(kết quả theo thứ tự ngẫu nhiên)

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

trong đó grep thực hiện tất cả các công việc nặng: báo cáo từng ký tự được tìm thấy ở mỗi số dòng. Phần còn lại chỉ là tổng số đếm trên mỗi dòng và định dạng đầu ra.

Gỡ bỏ -n và lấy số đếm cho toàn bộ tập tin.

Đếm một tệp văn bản 1,5Meg trong dưới 0,015 giây có vẻ nhanh.
[.__.] Và không hoạt động với các ký tự (không phải byte).

1
user79743

Một giải pháp cho bash. Không có chương trình bên ngoài được gọi (nhanh hơn cho chuỗi ngắn).

Nếu giá trị nằm trong một biến:

$ a='"Hello!"'

Điều này sẽ in bao nhiêu " Nó chứa:

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