是否有一个内置命令来执行此操作,或者有任何人对运行它的脚本有任何好运?
我希望了解有多少记录(由特定的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)或正常字符输入(即|),则可获得奖励。
答案 0 :(得分: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
答案 1 :(得分: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
”行并将say
替换为print
)
答案 2 :(得分:0)
嗯,文件末尾还有一个空记录,其中有0个。所以,这是一个可以做你想要的脚本。添加标题和以其他方式调整printf输出仍然是一个练习。 :)
基本上,读取整个文件,将其拆分为记录,对于每个记录,使用/ g正则表达式来计算子分隔符。由于/ g返回所有匹配的数组,因此使用@ {[]}生成一个arrayref,然后在标量上下文中对其进行deref以获取计数。对问题的这个特定部分必须有一个更优雅的解决方案,但无论如何;这是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);
答案 3 :(得分: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])
}
}