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 条记录有一个分隔符,第 2 条记录有 2,第 3 条记录有 0,第 4 条记录有 3,第 5 条记录有 1,第 6 条记录有 0。

如果您可以使分隔符和 EOL 参数接受十六进制输入(即 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

这就是 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。所以,这里有一个脚本来做你想做的事。添加标题和以其他方式调整 printf 输出留给您作为练习。:)

基本上,读入整个文件,将其拆分为记录,对于每条记录,使用 /g 正则表达式来计算子分隔符。由于 /g 返回一个包含所有匹配项的数组,请使用 @{[]} 创建一个数组引用,然后在标量上下文中取消引用它以获取计数。对于问题的特定部分,必须有一个更优雅的解决方案,但无论如何;这是 perl 线路噪音。;)

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

这是一个修改后的版本,它只保留至少包含一个非空格字符的字段。这会删除最后一个字段,但也会导致删除任何其他空字段。它还使用 $/ 和 \Q\E 来减少几个显式函数调用(感谢 Alex)。而且,和前一个一样,它适用于严格的 + 警告;

#!/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 回答
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 回答