4

これを行うための組み込みコマンドはありますか、それともそれを行うスクリプトで運が良かった人はいますか?

特定の文字が何回出現したか ("^%!" などの特定の EOL によって定義される) のレコードの数を取得しようとしています。(出現回数の降順でソート)

たとえば、次のサンプル ファイルを使用します。

jdk,|ljn^%!dk,|sn,|fgc^%!
ydfsvuyx^%!67ds5,|bvujhy,|s6d75
djh,|sudh^%!nhjf,|^%!fdiu^%!

推奨される入力: EOL とファイル名を引数として区切ります。

bash/perl some_script_name ",|" "^%!" samplefile

望ましい出力:

occs    count
3        1
2        1
1        2
0        2

これは、1 番目のレコードには区切り文字が 1 つ、2 番目のレコードには 2、3 番目のレコードには 0、4 番目のレコードには 3、5 番目のレコードには 1、6 番目のレコードには 0 があったためです。

区切り文字と EOL 引数が 16 進入力 (つまり 2C7C) または通常の文字入力 (つまり ,|) を受け入れるようにすることができればボーナスポイント。

4

4 に答える 4

2

脚本:

#!/usr/bin/perl
use strict;

$/ = $ARGV[1];
open my $fh, '<', $ARGV[2] or die $!;
my @records = <$fh> and close $fh;

$/ = $ARGV[0];
my %counts;
$counts{(split $_)-1}++ for @records;
delete $counts{-1};

print "$_\t$counts{$_}\n" for (reverse sort keys %counts);

テスト:

perl script.pl ',|' '^%!' samplefile 

出力:

3   1
2   1
1   2
0   2
于 2012-04-15T13:19:28.057 に答える
0

のソリューションawk:

BEGIN {
    RS="\\^%!"
    FS=",\\|"
    max_occ = 0
} 
{
    if(match($0, "^ *$")) {  # This is here to deal with the final separator.
        next
    }

    if(NF - 1 > max_occ) {
        max_occ = NF - 1
    }
    count[NF - 1]=count[NF - 1] + 1
}
END {
    printf("occs    count\n")
    for(i = 0; i <= max_occ; i++) {
        printf("%s    %s\n", i, count[i])
    }
}
于 2012-04-15T13:09:29.613 に答える
0

これが perl の目的です:

#!perl -w
use 5.12.0;

my ($delim, $eol, $file) = @ARGV;

open my $fh, "<$file" or die "error opening $file $!";
$/ = $eol; # input record separator

my %counts;
while (<$fh>) {
    my $matches = () = $_ =~ /(\Q$delim\E)/g; # "goatse" operator
    $counts{$matches}++;
}

say "occs\tcount";
foreach my $num (reverse sort keys %counts) {
    say "$num\t$counts{$num}";
}

(5.12 を取得していない場合は、" " 行を削除してにuse 5.12置き換えます)sayprint

于 2012-04-15T04:06:31.103 に答える
0

さて、ファイルの最後に 0 を持つ空のレコードがもう 1 つあります。ヘッダーの追加やその他の printf 出力の微調整は、練習問題として残されています。:)

基本的に、ファイル全体を読み取り、レコードに分割し、レコードごとに /g 正規表現を使用してサブ区切り文字を数えます。/g はすべての一致の配列を返すため、@{[]} を使用して配列参照を作成し、スカラー コンテキストでそれを逆参照してカウントを取得します。問題のその特定の部分に対するより洗練された解決策が必要ですが、何でも構いません。パールラインノイズです。;)

user@host[/home/user]
$ ./test.pl ',|' '^%!' test.in
3   1
2   1
1   2
0   3
user@host[/home/user]
$ cat test.in
jdk,|ljn^%!dk,|sn,|fgc^%!
ydfsvuyx^%!67ds5,|bvujhy,|s6d75
djh,|sudh^%!nhjf,|^%!fdiu^%!
user@host[/home/user]
$ cat test.pl
#!/usr/bin/perl

my( $subdelim, $delim, $in,) = @ARGV;
$delim = quotemeta $delim;
$subdelim = quotemeta $subdelim;
my %counts;

open(F, $in) or die qq{Failed opening $in: $?\n};
foreach( split(/$delim/, join(q{}, <F>)) ){
  $counts{ scalar(@{[m/.*?($subdelim)/g]}) }++;
}
printf( qq{%i% 4i\n}, $_, $counts{$_} ) foreach (sort {$b<=>$a} keys %counts);

これは、少なくとも 1 つの非スペース文字を含むフィールドのみを保持する修正バージョンです。これにより、最後のフィールドが削除されますが、他の空のフィールドも削除されるという結果になります。また、$/ と \Q\E を使用して、いくつかの明示的な関数呼び出しを減らします (ありがとう、アレックス)。そして、前のものと同様に、strict + warnings で動作します。

#!/usr/bin/perl

my( $subdelim, $delim, $in ) = @ARGV;
local $/=$delim;

my %counts;
open(F, $in) or die qq{Failed opening $in: $?\n};
foreach ( grep(/\S/, <F>) ){
  $counts{ scalar(@{[m/.*?(\Q$subdelim\E)/g]}) }++;
}
printf( qq{%i% 4i\n}, $_, $counts{$_} ) foreach (sort {$b<=>$a} keys %counts);

本当に最後のレコードだけを無条件に削除したい場合、私は pop の使用に部分的です:

#!/usr/bin/perl

my( $subdelim, $delim, $in ) = @ARGV;
local $/=$delim;

my %counts;
open(F, $in) or die qq{Failed opening $in: $?\n};
my @lines = <F>;
pop @lines;
$counts{ scalar(@{[m/.*?(\Q$subdelim\E)/g]}) }++ foreach (@lines);
printf( qq{%i% 4i\n}, $_, $counts{$_} ) foreach (sort {$b<=>$a} keys %counts);
于 2012-04-15T04:16:15.710 に答える