Answers:
$ awk '{ print length }' file | sort -n | uniq -c | awk '{ printf("%d character words: %d\n", $2, $1) }'
2 character words: 3
5 character words: 1
7 character words: 1
第一个awk
过滤器只会打印名为的文件中每行的长度file
。我假设此文件每行包含一个单词。
的sort -n
(来自的输出排序线awk
和数值在升序)uniq -c
(计数次数每一行连续出现的数目),然后将从该给定数据创建的输出如下:
3 2
1 5
1 7
然后,由第二个awk
脚本对此进行解析,该脚本将每行解释为“具有Y个字符的X行数”,并生成所需的输出。
另一种解决方案是全部完成awk
并在数组中保留长度计数。在效率,可读性/易于理解(以及因此的可维护性)之间进行权衡,哪种解决方案是“最佳”的。
替代解决方案:
$ awk '{ len[length]++ } END { for (i in len) printf("%d character words: %d\n", i, len[i]) }' file
2 character words: 3
5 character words: 1
7 character words: 1
awk
独自完成所有事情的另一种方式
$ awk '{words[length()]++} END{for(k in words)print k " character words - " words[k]}' ip.txt
2 character words - 3
5 character words - 1
7 character words - 1
words[length()]++
使用输入线的长度作为键来保存计数END{for(k in words)print k " character words - " words[k]}
处理完所有行后,以所需格式打印数组的内容
性能比较,选择的数字是两次运行中最好的
$ wc words.txt
71813 71813 655873 words.txt
$ perl -0777 -ne 'print $_ x 1000' words.txt > long_file.txt
$ du -h --apparent-size long_file.txt
626M long_file.txt
$ time awk '{words[length()]++} END{for(k in words)print k " character words - " words[k]}' long_file.txt > t1
real 0m20.632s
user 0m20.464s
sys 0m0.108s
$ time perl -lne '$h{length($_)}++ }{ for $n (sort keys %h) {print "$n character words - $h{$n}"}' long_file.txt > t2
real 0m19.749s
user 0m19.640s
sys 0m0.108s
$ time awk '{ print length }' long_file.txt | sort -n | uniq -c | awk '{ printf("%d character words - %d\n", $2, $1) }' > t3
real 1m23.294s
user 1m24.952s
sys 0m1.980s
$ diff -s <(sort t1) <(sort t2)
Files /dev/fd/63 and /dev/fd/62 are identical
$ diff -s <(sort t1) <(sort t3)
Files /dev/fd/63 and /dev/fd/62 are identical
如果文件只有ASCII字符,
$ time LC_ALL=C awk '{words[length()]++} END{for(k in words)print k " character words - " words[k]}' long_file.txt > t1
real 0m15.651s
user 0m15.496s
sys 0m0.120s
不知道为什么时间perl
没有太大变化,可能必须以其他方式设置编码
length
()
在这里,没有工作非常完美,因此添加大括号可能是多余的。我正在使用GNU awk。
In older versions of awk, the length() function could be called without any parentheses. Doing so is considered poor practice, although the 2008 POSIX standard explicitly allows it, to support historical practice. For programs to be maximally portable, always supply the parentheses
这是perl
等效项(带有-可选-排序):
$ perl -lne '
$h{length($_)}++ }{ for $n (sort keys %h) {print "$n character words - $h{$n}"}
' file
2 character words - 3
5 character words - 1
7 character words - 1
{$a<=>$b}
之后添加sort
将解决该问题。或者,可以使用带有数字键的普通数组,而跳过值为零/未定义的任何键。
使用printf对GNU awk 的另一种调用:
$ awk 'BEGIN { PROCINFO["sorted_in"] = "@ind_str_asc"}
{c[length($0)]++}
END{
for(i in c){printf("%s character words - %s\n",i,c[i])}
}' infile
2 character words - 3
5 character words - 1
7 character words - 1
核心算法仅收集数组中的字符数。末尾部分打印以printf格式化的收集到的计数。
快速,简单,一次调用awk。
准确地说:使用更多的内存来保留阵列。
但是不会调用任何排序方法(将数字数组索引设置为始终使用PROCINFO向上遍历排序),并且只有一个外部程序:awk
,而不是多个。
for in
可能至少会为某些值或某些awk实现提供按数字顺序的数字数组索引,但这不是必需的,不是传统的,而且绝对不是通用的。对于2或3或4的微小集合,通常会发生这种情况;在您有权访问的每个awk上尝试10或20(在gawk中没有PROCINFO或WHINY_USERS),我敢打赌50美元,至少一种情况没有排序。
@ind_str_asc
排序为字符串,仅当数字都是一位数字(如您的示例)时,才对数字正确;使用@ind_num_asc
if(any)值可以为10或更大。尽管现在已经不是以前的问题了,但此功能仅适用于gawk 4.0 up。