3

ファイル/行で特定の単語が何回言及されたかを確認したい。

私のダミーの例は次のようになります。

cat words
blue
red 
green
yellow 

cat text
TEXTTEXTblueTEXTTEXTblue
TEXTTEXTgreenblueTEXTTEXT
TEXTTEXyeowTTEXTTEXTTEXT

私はこれをやっています:

for i in $(cat words); do grep "$i" text | wc >> output; done

cat output
  2       2      51
  0       0       0
  1       1      26
  0       0       0

しかし、私が実際に取得したいのは次のとおり
です。1.変数として使用された単語。
2. (テキストヒットに加えて) 何行で単語が見つかったか。

好ましい出力は次のようになります。

blue    3   2
red     0   0 
green   1   1
yellow  0   0

$1 - grep
された変数 $2 - テキスト内で変数が見つかった回数
$3 - 変数が見つかった行数

grep、awk、sed は大規模なデータ セットに対して十分に高速であるため、誰かがこれを行うのを手伝ってくれることを願っていますが、Perl のワンライナーも役に立ちます。

編集

これを試した

   for i in $(cat words); do grep "$i" text > out_${i}; done && wc out*  

ちょっと見栄えはいいのですが、一部の単語は300文字を超えているため、単語のような名前のファイルを作成できません。

4

5 に答える 5

4

一致する行の一致した部分のみを出力し、各一致を別の出力行に出力するgrep オプション-oを使用できます。

while IFS= read -r line; do
    wordcount=$(grep -o "$line" text | wc -l)
    linecount=$(grep -c "$line" text)
    echo $line $wordcount $linecount
done < words | column -t

すべてを 1 行にまとめて 1 つのライナーにすることができます。

column で「column too long」エラーが発生した場合、最大文字数がわかっている場合は printf を使用できます。代わりに以下を使用してecho、パイプから列を削除します。

printf "%-20s %-2s %-2s\n" "$line" $wordcount $linecount

20 を最大語長に置き換え、必要に応じて他の数値も置き換えます。

于 2013-01-26T10:51:44.073 に答える
3

同様の Perl ソリューションを次に示します。むしろ完全なスクリプトとして書かれています。

#!/usr/bin/perl

use 5.012;

die "USAGE: $0 wordlist.txt [text-to-search.txt]\n" unless @ARGV;

my $wordsfile = shift @ARGV;
my @wordlist = do {
    open my $words_fh, "<", $wordsfile or die "Can't open $wordsfile: $!";
    map {chomp; length() ? $_ : ()} <$words_fh>;
};

my %words;
while (<>) {
    for my $word (@wordlist) {
        my $cnt = 0;
        $cnt++ for /\Q$word\E/g;
        $words{$word}[0] += $cnt;
        $words{$word}[1] += 1&!! $cnt; # trick to force 1 or 0.
    }
}

# sorts output after frequency. remove `sort {...}` to get unsorted output.
for my $key (sort {$words{$b}->[0] <=> $words{$a}->[0] or $a cmp $b} keys %words) {
    say join "\t", $key, @{ $words{$key} };
}

出力例:

blue    3       2
green   1       1
red     0       0
yellow  0       0

bash スクリプトに対する利点: すべてのファイルは 1 回だけ読み取られます。

于 2013-01-26T13:16:41.493 に答える
1

awk(gawk) ワンライナーは grep パズルからあなたを救うことができます:

  awk 'NR==FNR{n[$0];l[$0];next;}{for(w in n){ s=$0;t=gsub(w,"#",s); n[w]+=t;l[w]+=t>0?1:0;}}END{for(x in n)print x,n[x],l[x]}' words text

コードを少しフォーマットします。

awk 'NR==FNR{n[$0];l[$0];next;}
    {for(w in n){ s=$0;
        t=gsub(w,"#",s); 
        n[w]+=t;l[w]+=t>0?1:0;}
    }END{for(x in n)print x,n[x],l[x]}' words text

あなたの例でテストしてください:

kent$  awk 'NR==FNR{n[$0];l[$0];next;}{for(w in n){ s=$0;t=gsub(w,"#",s); n[w]+=t;l[w]+=t>0?1:0;}}END{for(x in n)print x,n[x],l[x]}' words text
yellow  0 0
red  0 0
green 1 1
blue 3 2

出力をフォーマットしたい場合は、awk出力をパイプするだけですcolumn -t

次のようになります。

yellow  0  0
red     0  0
green   1  1
blue    3  2
于 2013-01-26T19:56:46.633 に答える
1

これは、Perl のワンライナーとしてはかなり醜いものになります (2 つのファイルからデータを取得する必要があり、stdin で送信できるのは 1 つだけだからです。また、一致した行数と一致した合計数の両方をカウントする必要があるためです) 、しかし、ここに行きます:

perl -E 'undef $|; open $w, "<", "words"; @w=<$w>; chomp @w; $r{$_}=[0,{}] for @w; my $re = join "|", @w; while(<>) { $l++; while (/($re)/g) { $r{$1}[0]++; $r{$1}[1]{$l}++; } }; say "$_\t$r{$_}[0]\t" . scalar keys %{$r{$_}[1]} for @w' < text

これには perl 5.10 以降が必要ですが、5.8 以前をサポートするように変更するのは簡単です。( を に変更-E-e、 に変更sayし、出力の各行の最後にprinta を追加します。)\n

出力:

blue    3   2
red     0   0
green   1   1
yellow  0   0
于 2013-01-26T12:54:39.537 に答える
1
awk '
NR==FNR { words[$0]; next }
{
   for (word in words) {
      count = gsub(word,word)
      if (count) {
         counts[word] += count
         lines[word]++
      }
   }
}
END { for (word in words) printf "%s %d %d\n", word, counts[word], lines[word] }
' file
于 2013-01-27T12:37:31.517 に答える