प्रत्येक पंक्ति में एक विशिष्ट वर्ण की संख्या कैसे गिना जाए?


87

मैं सोच रहा था कि कुछ पाठ प्रसंस्करण उपयोगिताओं द्वारा प्रत्येक पंक्ति में एक विशिष्ट चरित्र की संख्या कैसे गिना जाए?

उदाहरण के लिए, "निम्नलिखित पाठ की प्रत्येक पंक्ति में गणना करने के लिए

"hello!" 
Thank you!

पहली पंक्ति में दो और दूसरी पंक्ति में 0 है।

एक और उदाहरण (प्रत्येक पंक्ति में गिनना है।


1
बस यह जोड़ने जा रहा है कि आपने सीड के साथ नियमित अभिव्यक्तियों का उपयोग करने के बजाय इसके लिए अपना 10 लाइन सी प्रोग्राम लिखकर बहुत अधिक प्रदर्शन प्राप्त किया। आपको अपनी इनपुट फ़ाइलों के आकार के आधार पर करने पर विचार करना चाहिए।
user606723

जवाबों:


104

आप इसके साथ कर सकते हैं sedऔर awk:

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

datआपका उदाहरण टेक्स्ट कहां है, प्रत्येक लाइन के लिए sed डिलीट (प्रत्येक लाइन के लिए) सभी गैर- "वर्ण और awkप्रत्येक लाइन के लिए प्रिंट (इसका अर्थ इसके lengthबराबर है length($0), जहां $0वर्तमान लाइन को दर्शाता है)।

एक और चरित्र के लिए आपको बस sed अभिव्यक्ति को बदलना होगा। इसके लिए उदाहरण के (लिए:

's/[^(]//g'

अद्यतन: sed कार्य के लिए ओवरकिल की तरह है - trपर्याप्त है। इसके साथ एक समान समाधान trहै:

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

मतलब कि trसभी वर्णों को नष्ट कर देता है जो कि ( -cमतलब पूरक) वर्ण सेट में नहीं हैं "\n


3
+1 तुलना में अधिक कुशल होना चाहिए trऔर wcसंस्करण।
स्टीफन जिमेनेज़

1
हां, लेकिन क्या यह यूनिकोड को संभाल सकता है?
एम्फ़ैटेमाचिन

@amphetamachine, हाँ - कम से कम एक क्विक टेस्ट ß(utf hex: c3 9f) (के बजाय ") अपेक्षित रूप से काम करता है, यानी tr, sedऔर awkसमस्या के बिना पूरक / प्रतिस्थापन / गिनती करता है - एक Ubuntu 10.04 सिस्टम पर।
मैक्सक्लेपजिग

1
trGNU tr और क्लासिक यूनिक्स tr सहित, के अधिकांश संस्करण सिंगल बाइट वर्णों पर काम करते हैं और यूनिकोड के अनुरूप नहीं हैं .. विकिपीडिया tr (Unix) से उद्धृत .. इस स्निपेट को आज़माएं: echo "aā⧾c" | tr "ā⧾" b... Ubuntu 10.04 पर ... ßएक एकल बाइट है विस्तारित लैटिन चार्ट और इसके द्वारा नियंत्रित किया जाता है tr... यहां असली मुद्दा यह नहीं है कि trयूनिकोड को संभालना नहीं है (क्योंकि सभी वर्ण यूनिकोड हैं), यह वास्तव में है कि trकेवल एक बार में एक-बाइट को संभालता है ..
पीटर.ओ.

@fred, नहीं, ß एक भी बाइट कैरेक्टर नहीं है - इसकी यूनिकोड स्थिति U + 00DF है, जिसे UTF-8 में 'c3 9f' अर्थात दो बाइट्स के रूप में कोडित किया गया है ।
maxschlepzig

49

मैं बस awk का उपयोग करूंगा

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

यहां हम फ़ील्ड विभाजक को (-F ध्वज के साथ) वर्ण के रूप में सेट करते हैं, "फिर हम सभी फ़ील्ड की संख्या प्रिंट करते हैं NF- 1. लक्ष्य वर्ण की घटनाओं की संख्या अलग-अलग फ़ील्ड की संख्या से एक कम होगी।

मज़ेदार पात्रों के लिए, जो शेल द्वारा व्याख्या किए जाते हैं, आपको केवल यह सुनिश्चित करने की आवश्यकता है कि आप उनसे बच सकते हैं अन्यथा कमांड लाइन उन्हें कोशिश और व्याख्या करेगी। तो दोनों के लिए "और )आपको क्षेत्र विभाजक (साथ \) से बचने की आवश्यकता है ।


1
शायद भागने के बजाय एकल उद्धरण का उपयोग करने के लिए अपने उत्तर को संपादित करें। यह किसी भी चरित्र (छोड़कर ') के साथ काम करेगा । साथ ही, इसमें खाली लाइनों के साथ एक अजीब व्यवहार है।
स्टीफन जिमेनेज़

प्रश्न विशेष रूप से उपयोग करता है "इसलिए मुझे लगता है कि इसके साथ कोड काम करने के लिए बाध्य है। यह निर्भर करता है कि आप किस शेल का उपयोग कर रहे हैं, जिस मौसम में चरित्र को बचाना है, लेकिन बैश / tcsh दोनों को बचना होगा "
मार्टिन

बेशक, लेकिन कोई समस्या नहीं है -F'"'
स्टेफेन जिमेनेज

+1 एफएस का उपयोग करने के लिए एक अच्छा विचार क्या है ... यह -1 को दिखाने वाली रिक्त लाइन को हल करेगा, और उदाहरण के लिए, बैश कमांडलाइन से "$ 1"। ...awk -F"$1" '{print NF==0?NF:NF-1}' filename
पीटर।

विभाजक के रूप में कई वर्णों के साथ भी काम करें ... उपयोगी!
कॉयल

14

trArd का उपयोग करना wc:

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

उपयोग:

$ 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

3
ध्यान दें। trएक से अधिक बाइट का उपयोग करने वाले वर्णों को हैंडल नहीं करता है .. विकिपीडिया tr (यूनिक्स) देखें .. अर्थात। trयूनिकोड अनुरूप नहीं है।
पीटर।


आपको व्हॉट्सएप के पात्रों को हटाने की आवश्यकता है $IFS, अन्यथा readउन्हें प्रारंभ और अंत से ट्रिम कर दिया जाएगा।
स्टीफन चेज़लस


@ पीटर.ओ, कुछ trकार्यान्वयन मल्टीबाइट वर्णों का समर्थन करते हैं, लेकिन wc -cबाइट्स को गिनते हैं, वैसे भी वर्ण नहीं ( wc -mवर्णों की आवश्यकता )।
स्टीफन चेज़लस

11

फिर भी एक और कार्यान्वयन कि बाहरी प्रोग्राम पर निर्भर नहीं करता, में bash, zsh, yashके कुछ कार्यान्वयन / संस्करणों और ksh:

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

line="${line//[!(]}"गिनती के लिए उपयोग करें (


जब अंतिम पंक्ति में एक अनुगामी \ n नहीं होता है, जबकि लूप बाहर निकलता है, क्योंकि यद्यपि यह अंतिम पंक्ति पढ़ता है, यह EOF को इंगित करने के लिए एक गैर-शून्य निकास कोड भी देता है ... इसके चारों ओर पाने के लिए, निम्नलिखित स्निपेट काम करता है (.. यह थोड़ी देर के लिए मुझे गुस्सा eof=false; IFS=; until $eof; do read -r || eof=true; echo "$REPLY"; done
दिला

@ गिल्स: आपने एक अनुगामी जोड़ा है जिसकी /आवश्यकता बैश में नहीं है। यह एक ksh आवश्यकता है?
enzotib

1
अनुगामी /के साथ-साथ पार्टी के पुराने संस्करणों में IIRC ksh के पुराने संस्करणों में की जरूरत है, और।
गिल्स

10

awkयदि मैचों की संख्या बहुत बड़ी है (जो मेरी स्थिति में होती है) विफल हो जाती हैं तो उत्तर का उपयोग करना विफल हो जाता है। लोकी-अस्तिरी से जवाब के लिए , निम्नलिखित त्रुटि बताई गई है:

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

से जवाब के लिए enzotib (और से बराबर manatwork ), एक विभाजन दोष होता है:

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

sedद्वारा समाधान maxschlepzig सही ढंग से काम करता है, लेकिन धीमी गति से (नीचे समय) है।

कुछ समाधान अभी तक यहां नहीं सुझाए गए हैं। सबसे पहले, का उपयोग कर grep:

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

और उपयोग कर रहा है perl:

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

यहां कुछ समाधानों के लिए कुछ समय दिए गए हैं (सबसे धीमी गति से सबसे तेज़ ऑर्डर किए गए); मैंने चीजों को यहां वन-लाइनर्स तक सीमित कर दिया। 'foo.txt' एक फाइल है जिसमें एक लाइन और एक लंबी स्ट्रिंग होती है जिसमें 84922 मैच होते हैं।

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

+ अच्छा विचार है! मैंने आपकी तालिका का विस्तार किया, एक नए उत्तर में, संपादित करने के लिए स्वतंत्र महसूस करें (अंतिम चित्र इतना स्पष्ट नहीं है, लेकिन मेरा मानना ​​है कि @maxschlepzig स्टील तेज समाधान है)
JJoao

maxschlepzig का समाधान सुपर फास्ट है!
बजे


8

Awk और gsub के साथ एक और संभावित कार्यान्वयन:

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

फ़ंक्शन gsubsed के बराबर है 's///g'

gsub("[^(]", "")गिनती के लिए उपयोग करें (


आप एक चरित्र को बचा सकते हैं, यानी जब
स्टड

@maxschlepzig: हाँ, निश्चित रूप से;)
enzotib

1
awk '{print gsub(/"/,"")}' input-fileपर्याप्त होगा, जैसा कि "प्रत्येक विकल्प के लिए स्ट्रिंग टी में नियमित अभिव्यक्ति आर से मेल खाते हुए, स्ट्रिंग एस को प्रतिस्थापित करें, और प्रतिस्थापन की संख्या वापस करें।" (man awk)
मैनटवर्क

6

मैंने सी प्रोग्राम लिखने का फैसला किया क्योंकि मैं बोर हो गया था।

आपको संभवतः इनपुट सत्यापन जोड़ना चाहिए, लेकिन इसके अलावा अन्य सभी सेट हैं।

#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);
}

धन्यवाद! बोर होने के लिए धन्यवाद ताकि मैं कुछ सीख सकूं। ओह रुको, क्या आपको वापसी की आवश्यकता है?
टिम

* श्रग्स * , यदि आप पूरी तरह से सही होना चाहते हैं, तो आपको कुछ और # पिन जोड़ने की भी आवश्यकता है, लेकिन मेरे कंपाइलर पर डिफ़ॉल्ट चेतावनियों की परवाह नहीं है।
user606723

आप बाहर छोड़ सकते हैं free(line)क्योंकि कार्यक्रम से बाहर निकलने से तात्पर्य सभी आवंटित मेमोरी से मुक्त हो जाता है - फिर एक के लिए जगह है return 0;...)। यहां तक ​​कि उदाहरणों में, रिटर्न कोड को अपरिभाषित छोड़ना अच्छी शैली नहीं है। Btw, getlineएक GNU एक्सटेंशन है - यदि कोई सोच रहा है तो।
मैक्सचेलपिजिग

@maxschlepzig: क्या मेमोरी को लाइनलाइन द्वारा आवंटित किया गया है ()? क्या यह गतिशील रूप से मालॉक द्वारा ढेर पर या स्टैक पर सांख्यिकीय रूप से आवंटित किया गया है? आपने कहा कि यह आवश्यक नहीं है, इसलिए इसे गतिशील रूप से आवंटित नहीं किया गया है?
टिम

1
@ टिम, हां, उदाहरण के लिए यदि आप कोड को ऐसे रिफ्लेक्टर करते हैं कि यह एक स्टैंडअलोन फ़ंक्शन है - तो - - f, जिसे दूसरे कोड से कई बार कहा जाता है, तो आपको इस फ़ंक्शन के अंत में freeअंतिम कॉल के बाद कॉल getlineकरना होगा f
maxschlepzig

6

एक स्ट्रिंग के लिए, सबसे सरल के साथ होगा trऔर wc(के साथ overkill करने की आवश्यकता नहीं है ) awkया sed- लेकिन उपरोक्त टिप्पणियों पर ध्यान दें tr, बाइट्स गिनता है, वर्ण नहीं -

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

$xवह चर कहां है जिसमें मूल्यांकन करने के लिए स्ट्रिंग (फाइल नहीं) है।


4

यहाँ एक और C समाधान है जो केवल STD C और कम मेमोरी की आवश्यकता है:

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

यह अंतिम पंक्ति पर रिपोर्ट नहीं करेगा यदि इसके पास अनुगामी '\ n' न हो
पीटरऑ।

1
@fred, हाँ, जो उद्देश्य पर है, क्योंकि अनुगामी के बिना एक \nरेखा कोई वास्तविक रेखा नहीं है। यह मेरे अन्य सेड / awk (tr / awk) उत्तर के समान व्यवहार है।
maxschlepzig

3

हम उपयोग कर सकते हैं grepके साथ regexइसे और अधिक सरल और शक्तिशाली बनाने के लिए।

विशिष्ट चरित्र की गणना करने के लिए।

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

व्हॉट्सएप पात्रों सहित विशेष वर्णों की गणना करने के लिए।

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

यहां हम किसी भी वर्ण का चयन कर रहे हैं [\S\s]और -oविकल्प के साथ हम grepप्रत्येक मैच (जो प्रत्येक वर्ण है) को अलग-अलग पंक्ति में प्रिंट करने के लिए बनाते हैं । और फिर wc -lप्रत्येक पंक्ति को गिनने के लिए उपयोग करें।


OP किसी फ़ाइल में सभी वर्णों की संख्या मुद्रित नहीं करना चाहते हैं! वह एक विशिष्ट वर्ण की संख्या की गिनती / प्रिंट करना चाहता है। उदाहरण के लिए "प्रत्येक पंक्ति में कितने हैं; और किसी भी अन्य वर्ण के लिए। उसका सवाल देखें और जवाब भी स्वीकार करें।
α atsнιη

3

शायद एक और अधिक सीधे आगे, विशुद्ध रूप से awk उत्तर विभाजन का उपयोग करना होगा। स्प्लिट एक स्ट्रिंग लेता है और इसे एक सरणी में बदल देता है, वापसी मान + 1 उत्पन्न सरणी आइटम की संख्या है।

निम्न कोड प्रत्येक पंक्ति पर दिखाई देने वाले समय को "प्रिंट करेगा"।

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

विभाजन की अधिक जानकारी http://www.staff.science.uu.nl/~oostr102/docs/nawk/nawk_92.html


2

"फ़ाइल की प्रत्येक पंक्ति में गिनती जानने के लिए एक साधारण पायथन लिपि है :

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

यहां हमने countअंतर्निहित strप्रकार की विधि का उपयोग किया है ।


2

शुद्ध बैश समाधान के लिए (हालांकि, यह बैश-विशिष्ट है): यदि $xचर आपके स्ट्रिंग वाला है:

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

${x//बात को छोड़ कर सभी वर्ण निकालता है ", ${#x2}इस बाकी की लंबाई की गणना करता है।

(मूल सुझाव exprजिसके उपयोग से समस्याएं हैं, टिप्पणियाँ देखें:)

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

ध्यान दें कि यह GNU के लिए विशिष्ट है exprऔर बाइट्स गिनता है, वर्ण नहीं। अन्य के साथ expr:expr "x${x...}" : "x.*" - 1
स्टीफन चेज़लस

ओह ठीक है, धन्यवाद! मैंने इसे एक और विचार का उपयोग करके संशोधित किया है, जो मेरे पास एक बाहरी कार्यक्रम का उपयोग न करने का लाभ है।
मैरियन

2

aगिनने के लिए चार से बदलें । आउटपुट प्रत्येक पंक्ति के लिए काउंटर है।

perl -nE 'say y!a!!'

2

प्रस्तुत समाधानों की समय तुलना (उत्तर नहीं)

उत्तरों की दक्षता महत्वपूर्ण नहीं है। फिर भी, @josephwb दृष्टिकोण के बाद, मैंने प्रस्तुत सभी उत्तरों को समय देने की कोशिश की।

मैं विक्टर ह्यूगो "लेस मिसेबल्स" (महान पुस्तक!) के पुर्तगाली अनुवाद को इनपुट के रूप में उपयोग करता हूं और "ए" की घटनाओं को गिनता हूं। मेरे संस्करण में 5 खंड हैं, कई पृष्ठ ...

$ wc miseraveis.txt 
29331  304166 1852674 miseraveis.txt 

सी उत्तर जीसीसी, (कोई अनुकूलन) के साथ संकलित किए गए थे।

प्रत्येक उत्तर को 3 बार चलाया गया और सर्वश्रेष्ठ चुना गया।

इन नंबरों पर बहुत भरोसा न करें (मेरी मशीन अन्य कार्यों, आदि, आदि कर रही है)। मैं आपके साथ ये समय साझा करता हूं, क्योंकि मुझे कुछ अप्रत्याशित परिणाम मिले हैं और मुझे यकीन है कि आपको कुछ और मिलेगा ...

  • १४ समय के १४ समाधानों में १ से कम समय लगा; 9 कम तो 0.1 एस, उनमें से कई पाइप का उपयोग करते हैं
  • 2 समाधान, लाइन द्वारा बैश लाइन का उपयोग करके, नई प्रक्रियाएं बनाकर 30k लाइनों को संसाधित किया गया, 10/20 में सही समाधान की गणना करें।
  • grep -oP aपेड़ समय तेजी से होता है grep -o a (10; 11 बनाम 12)
  • सी और अन्य के बीच अंतर इतना बड़ा नहीं है जितना मुझे उम्मीद थी। ((;; बनाम २; ३)
  • (निष्कर्ष का स्वागत है)

(एक यादृच्छिक क्रम में परिणाम)

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

1
grep -n -o \" file | sort -n | uniq -c | cut -d : -f 1

जहां grep सभी भारी भार उठाता है: प्रत्येक पंक्ति संख्या पर पाए जाने वाले प्रत्येक वर्ण की रिपोर्ट करता है। बाकी सिर्फ प्रति पंक्ति गणना और आउटपुट को प्रारूपित करने के लिए है।

निकालें -nऔर पूरी फ़ाइल के लिए गणना प्राप्त करें।

0.015 सेकंड के तहत 1.5Meg पाठ फ़ाइल की गिनती तेजी से लगती है।
और पात्रों के साथ काम करता है (बाइट्स नहीं)।


1

बैश के लिए एक समाधान। कोई बाहरी कार्यक्रम नहीं कहा जाता है (छोटे तारों के लिए तेज़)।

यदि मान एक चर में है:

$ a='"Hello!"'

यह प्रिंट करेगा कि "इसमें कितने शामिल हैं:

$ b="${a//[^\"]}"; echo "${#b}"
2
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.