一部のテキスト処理ユーティリティで、各行の特定の文字の数を数える方法を知りましたか?
たとえば、"
次のテキストの各行
"hello!"
Thank you!
最初の行には2つ、2番目の行には0があります。
もう1つの例は、(
各行に。
sed
とawk
でそれを行うことができます:
_$ sed 's/[^"]//g' dat | awk '{ print length }'
2
0
_
ここで、dat
はテキストの例です。sedは、すべての非_"
_文字を(各行について)削除し、awk
は各行についてそのサイズを出力します(つまり、length
はlength($0)
に相当します。ここで、_$0
_は現在の行を示します)。
別のキャラクターの場合は、sedの式を変更するだけです。たとえば、_(
_の場合:
_'s/[^(]//g'
_
pdate:sed
はタスクのやり過ぎの一種です-tr
で十分です。 tr
を使用した同等のソリューションは次のとおりです。
_$ tr -d -c '"\n' < dat | awk '{ print length; }'
_
つまり、tr
は、文字セット_-c
_にない(_"\n
_は補数を意味する)すべての文字を削除します。
私はawkを使用します
awk -F\" '{print NF-1}' <fileName>
ここでは、フィールド区切り文字(-Fフラグを使用)を文字"
に設定し、フィールド数NF
-1を出力するだけです。ターゲット文字の出現回数は区切られたフィールドの数よりも1つ少ない。
シェルによって解釈される変な文字については、必ずエスケープする必要があります。そうしないと、コマンドラインがそれらを試みて解釈します。したがって、"
と)
の両方で、フィールド区切り文字をエスケープする必要があります(\
を使用)。
tr
ard 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
外部プログラムに依存しないさらに別の実装、bash
、zsh
、yash
およびksh
のいくつかの実装/バージョン:
while IFS= read -r line; do
line="${line//[!\"]/}"
echo "${#line}"
done <input-file
使用する line="${line//[!(]}"
カウント用(
。
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
maxschlepzig によるsed
ソリューションは正しく機能しますが、低速です(以下のタイミング)。
ここではまだ提案されていないいくつかのソリューション。まず、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
別のawk
ソリューション:
awk '{print gsub(/"/, "")}'
Awkとgsubを使用した別の可能な実装:
_awk '{ gsub("[^\"]", ""); print length }' input-file
_
関数gsub
は、sedの_'s///g'
_と同等です。
_(
_をカウントするには、gsub("[^(]", "")
を使用します。
退屈だったので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);
}
文字列の場合、最も単純なのはtr
とwc
です(awk
またはsed
でやり過ぎる必要はありません)。ただし、tr
に関する上記のコメントは、文字ではなくバイトをカウントします-
echo $x | tr -d -c '"' | wc -m
どこ $x
は、評価する文字列(ファイルではない)を含む変数です。
これは、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;
}
多分もっと単純で、純粋に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
grep
をregex
とともに使用すると、よりシンプルで強力になります。
特定の文字を数える。
$ grep -o '"' file.txt|wc -l
空白文字を含む特殊文字をカウントします。
$ grep -Po '[\W_]' file.txt|wc -l
ここでは、[\S\s]
を使用して任意の文字を選択し、-o
オプションを使用してgrep
を作成し、各一致(つまり、各文字)を別々の行に出力します。次に、wc -l
を使用して各行をカウントします。
純粋なbashソリューションの場合(ただし、bash固有です):$x
が文字列を含む変数の場合:
x2="${x//[^\"]/}"
echo ${#x2}
${x//
は"
を除くすべての文字を削除し、${#x2}
はこの残りの長さを計算します。
(問題のあるexpr
を使用した元の提案。コメントを参照してください:)
expr length "${x//[^\"]/}"
以下は、ファイルの各行の"
の数を見つけるための簡単なPythonスクリプトです。
#!/usr/bin/env python2
with open('file.txt') as f:
for line in f:
print line.count('"')
ここでは、組み込みcount
タイプのstr
メソッドを使用しました。
カウントする文字でa
を置き換えます。出力は各行のカウンターです。
Perl -nE 'say y!a!!'
提示されたソリューションの時間比較(回答ではありません)
回答の効率は重要ではありません。それにもかかわらず、@ josephwbアプローチに従って、提示されたすべての回答の時間を計ろうとしました。
私は入力として、Victor Hugoのポルトガル語訳「Les Miserables」(素晴らしい本!)を使用し、「a」の出現をカウントします。私のエディションには5巻、多くのページがあります...
$ wc miseraveis.txt
29331 304166 1852674 miseraveis.txt
Cの回答はgccでコンパイルされました(最適化なし)。
各回答は3回実行され、最良のものを選択しました。
これらの数値をあまり信用しないでください(私のマシンは他のタスクなどを実行しています)。私はこれらの時間をあなたと共有します、私はいくつかの予期しない結果を得たので、あなたはさらにいくつかを見つけると確信しています...
grep -oP a
はgrep -o a
より10倍高速です(10; 11 vs 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
grep -n -o \" file | sort -n | uniq -c | cut -d : -f 1
ここで、grepはすべての重い作業を行います。各行番号で見つかった各文字を報告します。残りは、1行あたりのカウントを合計し、出力をフォーマットすることです。
-n
を削除し、ファイル全体の数を取得します。
1.5Megのテキストファイルを0.015秒未満でカウントすると高速に見えます。
そして(バイトではなく)文字で動作します。
Bashのソリューション。外部プログラムは呼び出されませんでした(短い文字列の方が高速です)。
値が変数にある場合:
$ a='"Hello!"'
これにより、"
を含む:
$ b="${a//[^\"]}"; echo "${#b}"
2