各行の特定の文字の数を数える方法は?


87

一部のテキスト処理ユーティリティで各行の特定の文字の数をカウントする方法を知りたいのですが?

たとえば"、次のテキストの各行でカウントするには

"hello!" 
Thank you!

最初の行には2つあり、2番目の行には0があります。

別の例は(、各行でカウントすることです。


1
sedで正規表現を使用するのではなく、独自の10行のCプログラムを作成することにより、パフォーマンスが大幅に向上したことを付け加えます。入力ファイルのサイズに応じて実行することを検討する必要があります。
user606723

回答:


104

sedandでそれを行うことができます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はtrwcバージョンよりも効率的です。
ステファンギメネス

1
はい、ただしユニコードを処理できますか?
amphetamachine

@amphetamachine、はい-と、少なくとも簡単なテストß(UTF進:C3の9F)(代わりには"、すなわち、予想通り)作品trsedおよびawk-のUbuntu 10.04システム上で問題なくカウント/ /交換を補完します。
maxschlepzig

1
trGNU trおよび古典的なUnix trを含むほとんどのバージョンは、シングルバイト文字で動作し、Unicodeに準拠していません。Wikipediatr(Unix)から引用..このスニペットを試してください:echo "aā⧾c" | tr "ā⧾" b... Ubuntu 10.04で... ßはシングルバイトです拡張ラテン文字とによって処理されtr、ここで...本当の問題はないことがあるtrことは、(すべての文字がUnicodeであるため)、それは本当にあるのUnicodeに対応していないtrだけ一度に一つのバイトを処理します。..
Peter.O

@ fred、no、ßはシングルバイト文字ではありません-Unicodeの位置はU + 00DFで、UTF-8で「c3 9f」、つまり2バイトとしてコード化されています。
maxschlepzig

49

私はちょうどawkを使用します

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

ここでは、文字であることを(-Fフラグ付きで)フィールドセパレータを設定"し、我々が行うすべての分野のプリント枚数であるNF-対象文字の出現の1の数は、区切られたフィールドの数より1つ少なくなります。

シェルによって解釈されるおかしな文字については、エスケープする必要があります。そうしないと、コマンドラインがそれらを解釈しようとします。したがって、両方の場合、フィールド区切り文字をエスケープする必要が"あり)ます(で\)。


1
エスケープの代わりに一重引用符を使用するように回答を編集することもできます。任意の文字で動作します(を除く')。また、空の行で奇妙な動作をします。
ステファンギメネス

質問は具体的に使用している"ので、コードをそれで動作させる義務があります。それはあなたの文字をエスケープする必要がありますが、bashの/ tcshのは、両方とも「エスケープする必要があります。天候使用しているシェル何によって決まる
マーティンニューヨーク

もちろん、しかし問題ありません-F'"'
ステファンギメネス

+1 FSを使用することをお勧めします。これにより、-1を示す空白行と、たとえばbashコマンドラインの「$ 1」が解決されます。...awk -F"$1" '{print NF==0?NF:NF-1}' filename
Peter.O

セパレータとして複数の文字も使用できます...
コイル

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複数のバイトを使用する文字を処理しません。Wikipediatr(Unix)を参照してくださいtrUnicodeに準拠していません。
Peter.O


から空白文字を削除する必要があります。削除し$IFSないreadと、先頭と末尾から空白文字が削除されます。
ステファンシャゼル


@ Peter.O、一部のtr実装はマルチバイト文字をサポートしwc -cますが、とにかく文字ではなくバイトをカウントします(wc -m文字の必要性)。
ステファンシャゼル

11

しかし、外部プログラムに頼らない別の実装、でbashzshyashおよびいくつかの実装/バージョンのksh

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

line="${line//[!(]}"カウントに使用します(


最後の行に末尾の\ nがない場合、whileループは終了します。これは、最後の行を読み取ったにもかかわらず、EOF ...を示すゼロ以外の終了コードを返し、次のスニペットが機能するためです。 (..それはしばらくの間私を悩ませてきました、そして私はちょうどこのworkaroungを発見しました) eof=false; IFS=; until $eof; do read -r || eof=true; echo "$REPLY"; done
...-Peter.O

@Gilles:/bashには不要な末尾を追加しました。kshの要件ですか?
-enzotib

1
末尾/はkshの古いバージョンで必要であり、IIRCはbashの古いバージョンでも必要です。
ジル

10

awk一致の数が多すぎる場合(これが私の状況です)、使用する回答は失敗します。loki-astariからの回答については、次のエラーが報告されます。

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件の一致を含む1行と1つの長い文字列を含むファイルです。

## 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のソリューションは超高速です!
-okwap


8

awkとgsubを使用した別の可能な実装:

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

この関数gsubはsed'sと同等です's///g'

gsub("[^(]", "")カウントに使用します(


1文字を保存できます。つまり、stdinリダイレクトを削除するときなどです
;;

@maxschlepzig:ええ、もちろん;)
enzotib

1
awk '{print gsub(/"/,"")}' input-file「文字列tの正規表現rに一致する各部分文字列について、文字列sを置換し、置換の数を返します。」(男awk)
マナトワーク

6

私は退屈だったので、Cプログラムを書くことにしました。

おそらく入力検証を追加する必要がありますが、それ以外はすべて設定されています。

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

ありがとう!何かを学ぶことができるように退屈してくれてありがとう。待って、帰りが必要?
ティム

* shrugs *、完全に正確にしたい場合は、さらに#includeを追加する必要がありますが、コンパイラのデフォルトの警告は気にしないようです。
user606723

free(line)プログラムを終了すると、割り当てられたすべてのメモリが暗黙的に解放されるため、return 0;...を省略することができます。例でさえ、戻りコードを未定義のままにしておくのは良いスタイルではありません。ところで、getline誰かが疑問に思っている場合のために-GNU 拡張機能です。
maxschlepzig

@maxschlepzig:getline()によって割り当てられた行によってメモリがポイントされていますか?ヒープ上で動的に割り当てられますか、それともスタック上で静的に割り当てられますか?あなたはそれを解放する必要がないと言ったので、それは動的に割り当てられていませんか?
ティム

1
@Tim、はい、たとえばf、他のコードから数回呼び出されるスタンドアロン関数-say-になるようにコードをリファクタリングする場合、この関数のfree最後での最後の呼び出しの後に呼び出す必要がありgetlineますf
maxschlepzig

6

文字列の場合、最も単純なのはtrand でありwcawkまたはorでやりすぎの必要はありませんsed)-についての上記のコメントに注意してください、tr文字ではなくバイトをカウントします-

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

where $xは、評価する文字列(ファイルではない)を含む変数です。


4

STD Cと少ないメモリしか必要としない別の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'がない場合、これは最後の行を報告しません
Peter.O

1
@fred、はい、これは意図的です。なぜなら、末尾の\nない行は実際の行ではないからです。これは、他のsed / awk(tr / awk)の回答と同じ動作です。
maxschlepzig

3

grepwith regexを使用して、よりシンプルで強力にできます。

特定の文字を数えるため。

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

空白文字を含む特殊文字をカウントします。

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

ここでは、各一致(つまり、各文字)を別々の行に出力[\S\s]する-oオプションを使用grepして、任意の文字を選択しています。そして、wc -l各行のカウントに使用します。


OPはファイル内のすべての文字の数を印刷したくない!彼は特定のキャラクターの数をカウント/印刷したいと考えています。たとえば"、各行にいくつありますか。その他の文字用。彼の質問と受け入れられた答えを見てください。
αғsнιη

3

たぶんもっと単純な、純粋にawkの答えはsplitを使用することでしょう。Splitは文字列を受け取り、それを配列に変換します。戻り値は、生成された配列項目の数+ 1です。

次のコードは、各行に表示される回数を出力します。

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

分割に関する詳細http://www.staff.science.uu.nl/~oostr102/docs/nawk/nawk_92.html


2

"ファイルの各行のカウントを見つける簡単なPythonスクリプトを次に示します。

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

ここではcount、組み込みstr型のメソッドを使用しました。


2

純粋なbashソリューションの場合(ただし、bash固有です):$x文字列を含む変数の場合:

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

${x//事は除くすべての文字を削除し"${#x2}この残りの長さを計算します。

exprどちらに問題があるかについての元の提案、コメントを参照:)

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

これはGNUに固有exprであり、文字ではなくバイトをカウントすることに注意してください。他とexprexpr "x${x...}" : "x.*" - 1
ステファンシャゼル14年

そうそう、ありがとう!私が持っていた別のアイデアを使用して変更しました。これには、外部プログラムをまったく使用しないという利点があります。
マリアン

2

aカウントする文字に置き換えます。出力は、各行のカウンターです。

perl -nE 'say y!a!!'

2

提示されたソリューションの時間比較(答えではありません)

回答の効率は重要ではありません。それでも、@ josephwbのアプローチに従って、提示されたすべての回答の時間を測ろうとしました。

入力として、Victor Hugoのポルトガル語翻訳「Les Miserables」(素晴らしい本です!)を使用し、「a」の出現をカウントします。私のエディションには5つのボリューム、多くのページがあります...

$ wc miseraveis.txt 
29331  304166 1852674 miseraveis.txt 

Cの回答はgccでコンパイルされました(最適化なし)。

各回答は3回実行され、最適なものが選択されました。

これらの数値をあまり信用しないでください(私のマシンは他のタスクなどを実行しています)。予想外の結果が出たので、これらの時間をあなたと共有します。

  • 16のタイミングソリューションのうち14は1秒未満でした。9以下0.1秒、それらの多くはパイプを使用
  • bashを1行ずつ使用して、新しいプロセスを作成して3万行を処理し、10秒/ 20秒で正しいソリューションを計算する2つのソリューション。
  • grep -oP aツリー時間よりも速いgrep -o a (10; 11 vs 12)
  • Cと他の人との違いは、予想したほど大きくありません。(7; 8対2; 3)
  • (結論を歓迎)

(ランダムな順序になります)

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

bashのソリューション。外部プログラムは呼び出されません(短い文字列の場合は高速)。

値が変数にある場合:

$ a='"Hello!"'

これにより"、含まれている数が出力されます。

$ b="${a//[^\"]}"; echo "${#b}"
2
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.