是否有一种简便的方法可以从终端对文件中单词中的字符进行计数?


Answers:


20
$ 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排序(默认情况下,对数字索引数组进行排序)(更快)。
艾萨克(Isaac)

@箭头我知道。我在回答中注释了该解决方案,因为Sundeep在几秒钟内就击败了我。我在最后一段中也提到了这一点。
库萨兰达

我认为该评论对解决方案的用户很有用(未包含在您的答案(或Sundeep的回答:-)中)。否则:在您的回答中添加与您的评论相同的评论,我很乐意将其删除。:-)
艾萨克(Isaac)

10

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没有太大变化,可能必须以其他方式设置编码


只是将其添加到自己的解决方案中。当我看到你的时候把它删除了。:-)
库萨兰达

是的,我正在辩论删除我的,然后再次看到您的编辑:)
Sundeep

无需对数字索引数组进行排序。总是以增加的索引排序。(好吧,至少在awk :
Isaac

length()在这里,没有工作非常完美,因此添加大括号可能是多余的。我正在使用GNU awk。
谢尔盖·科洛迪亚兹尼

2
@SergiyKolodyazhnyy是的,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
Sundeep

5

这是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

如果键索引是数字键:键数组是否需要在Perl中排序?
艾萨克(Isaac)

1
@Arrow:这个答案使用的是散列(即带有字符串键的关联数组),并且那些键的顺序不确定,所以可以。实际上,答案有点错误,因为它会将键按字符串而不是数字排序。在{$a<=>$b}之后添加sort将解决该问题。或者,可以使用带有数字键的普通数组,而跳过值为零/未定义的任何键。
Ilmari Karonen

@IlmariKaronen谢谢,现在好了。大括号引起了什么变化!
艾萨克(Isaac)

使用数组而不是哈希会更有效。OP需要数百万行,因此可以通过便宜的索引轻松弥补打印时检查和跳过零的任何开销。
彼得·科德斯

5

使用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,而不是多个。


1
for in可能至少会为某些值或某些awk实现提供按数字顺序的数字数组索引,但这不是必需的,不是传统的,而且绝对不是通用的。对于2或3或4的微小集合,通常会发生这种情况;在您有权访问的每个awk上尝试10或20(在gawk中没有PROCINFO或WHINY_USERS),我敢打赌50美元,至少一种情况没有排序。
dave_thompson_085

感谢您的输入。使用这个:我相信现在已经排序了。:-)
艾萨克(Isaac)

1
@ind_str_asc排序为字符串,仅当数字都是一位数字(如您的示例)时,才对数字正确;使用@ind_num_ascif(any)值可以为10或更大。尽管现在已经不是以前的问题了,但此功能仅适用于gawk 4.0 up
dave_thompson_085
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.