有没有一种简单的方法可以从终端计算文件中单词的字符数?

Gir*_*iri 8 text-processing

我的文件中有 1 亿行。

每行只有一列。

例如

aaaaa
bb
cc
ddddddd
ee
Run Code Online (Sandbox Code Playgroud)

我想列出字符数

像这样

2 character words - 3
5 character words - 1
7 character words - 1
Run Code Online (Sandbox Code Playgroud)

等等。

有什么简单的方法可以在终端中做到这一点吗?

Kus*_*nda 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
Run Code Online (Sandbox Code Playgroud)

第一个awk过滤器将只打印名为file. 我假设这个文件每行包含一个单词。

sort -n(来自的输出排序线awk和数值在升序)uniq -c(计数次数每一行连续出现的数目),然后将从该给定数据创建的输出如下:

   3 2
   1 5
   1 7
Run Code Online (Sandbox Code Playgroud)

然后由第二个awk脚本解析,该脚本将每一行解释为“X 个具有 Y 个字符的行”并产生所需的输出。


另一种解决方案是全部完成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
Run Code Online (Sandbox Code Playgroud)


Sun*_*eep 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
Run Code Online (Sandbox Code Playgroud)
  • 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
Run Code Online (Sandbox Code Playgroud)

如果文件只有 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
Run Code Online (Sandbox Code Playgroud)

不知道为什么时间perl没有太大变化,可能编码必须以其他方式设置

  • @SergiyKolodyazhnyy 是的,[gnu awk 手册](https://www.gnu.org/software/gawk/manual/html_node/String-Functions.html) 说`在旧版本的 awk 中,可以调用 length() 函数没有任何括号。这样做被认为是糟糕的做法,尽管 2008 POSIX 标准明确允许它支持历史实践。为了使程序具有最大的可移植性,请始终提供括号` (2认同)

ste*_*ver 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
Run Code Online (Sandbox Code Playgroud)


ImH*_*ere 5

一种调用 GNU awk,使用printf

$ 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
Run Code Online (Sandbox Code Playgroud)

核心算法只是收集数组中的字符数。最后部分打印收集的计数,格式为 printf。

快速、简单,一次调用 awk。

准确地说:使用更多内存来保存数组。
但是没有调用排序(数字数组索引设置为始终使用 PROCINFO 向上遍历排序),并且只有一个外部程序:awk,而不是几个。