Shell 实践 - 单词和字母计数更有趣

作者:Dave Taylor

如果您还记得一个月前,您会想起我收到了一封来自某人(暗示,暗示)的祝福邮件,询问

亲爱的 Dave,我希望在下次玩Hangman或其他文字游戏时获得优势。我想知道英语中最常见的单词是什么,书面材料中最常见的字母是什么。如果您能展示如何用 shell 脚本做到这一点,那对您的专栏很有用,但如果不能,您能指出一个在线资源吗?谢谢。——Mike R.

我从古腾堡计划档案馆 (gutenberg.org) 中抓取了三本书进行分析并用作测试输入:Bram Stoker 的Dracula,Charles A. Beard 和 Mary Ritter Beard 的History of the United States,以及 Jane Austen 的Pride and Prejudice

分析这些文本文件的显而易见的方法是使用 wc 命令,该命令显示,总共,我们正在查看 497,663 个单词,290 万个字符。

我们使用以下方法来识别最常见的单词

$ cat *txt | tr ' ' '\012' | \
     tr '[:upper:]' '[:lower:]' | \
     tr -d '[:punct:]' | grep -v '[^a-z]' | \
     sort | uniq -c | sort -rn | head

结果足以揭示在我们 50 万字的样本中出现的前十个单词按顺序是:the, of, and, to, in, a, i, was, that 和 it。

现在,让我们换个方向,分析字母频率。然后,我们将回到寻找有趣和不寻常的单词。

计算字母频率

计算字母频率的基本问题是:“如何将一个单词分解成单个字母,以便每行只有一个字母?” 事实证明,方便的 Linux 工具 fold 可以完全满足我们的需求

$ echo hello | fold -w1
h
e
l
l
o

做得好!(请注意,您不能使用 fmt 或类似的命令,因为即使您指定 -w1 作为宽度,它也适用于单词,而不是字符。)

从那里很容易跳到让 fold 分解文本文件中的每个单词,对结果进行排序,并使用我们的强大二人组uniq -c | sort -rn以获得我们想要的结果

$ fold -w1 < dracula.txt | sort | \
      uniq -c | sort -rn | head
157559
78409 e
56524 t
51608 a
50568 o
43453 n
41749 h
38150 s
37950 i
35001 r

空格是最常见的,但我们可以目视跳过它,而不是用另一个进程来复杂化我们的管道。

正如我在开头所说,E 是最常见的字母,但坦率地说,看到 T 是第二常见的字母令人惊讶。也许是因为我们没有补偿大小写?让我们再试一次

$ fold -w1 < dracula.txt | sort | \
      tr '[:lower:]' '[:upper:]' | uniq -c | \
      sort -rn | head -5
157559
78409 E
56524 T
51608 A
50568 O

等一下。我们不应该得到相同的结果!嗯...你能看出我哪里做错了吗?提示:看看管道中命令的顺序。

找到了吗?tr 需要出现在第一个 sort 命令之前,否则它会转换输出,但在输出已经被单独排序之后。我们还应该去除标点符号,这也可以用 tr 命令完成。这是一个更好的尝试

$ fold -w1 < dracula.txt | \
      tr '[:lower:]' '[:upper:]' | sort | \
      tr -d '[:punct:]' | uniq -c | \
      sort -rn | head
157559
79011 E
58618 T
53146 A
51122 O
43975 N
43501 H
43423 I
39296 S
35607 R

如果我们使用我们所有的三本书而不是仅仅Dracula,这个顺序会改变吗?让我们试试看

$ cat *.txt | fold -w1 | \
     tr '[:lower:]' '[:upper:]' | sort | \
     tr -d '[:punct:]' | uniq -c | \
     sort -rn | head
468727
273409 E
201726 T
175637 A
169836 O
158561 N
155910 I
135513 S
133927 R
127716 H

相同的结果!按频率顺序,字母在文本中出现的顺序如下:E T A O N I S R H D L C U M F W G P Y B V K X J Q Z。(我有点惊讶 J 出现得如此不频繁。)

现在您知道在Hangman中按什么顺序猜测字母了,如果仅此而已。

说到Hangman

在我们结束之前,让我们回顾一下语料库中的单词,只找到那些至少十个字母长且不经常出现的单词。这是我将如何做到的

$ cat *.txt | tr ' ' '\012' | \
     tr '[:upper:]' '[:lower:]' | \
     tr -d '[:punct:]' | tr -d '[0-9]' | \
     sort | uniq -c | sort -n | \
     grep -E '..................' | head
   1 abolitionists
   1 accommodation
   1 accommodations
   1 accomplishing
   1 accomplishments
   1 accountability
   1 achievements
   1 acknowledging
   1 acknowledgments
   1 acquaintanceship
   1 administrative
   1 advertisement

这为我们提供了英语中不经常出现的长单词——或者,至少在我们分析的 50 万字语料库中只出现一次的单词。

(真实告白:我只是在 grep 正则表达式中添加了越来越多的点,直到我几乎排除了所有结果。我也可以使用 .{10,} 来获得十个字符或更长的匹配项。)

然而,其中一些单词显然在日常用语中比在这些特定书籍中更常见,例如 advertisement,我确信它在正常的对话中,或者至少在我经常出没的圈子里,每 50 万个单词出现不止一次!

对于Hangman来说,真正好的方法是进一步应用字母频率规则,这样你就可以提取不经常出现的单词,然后计算出单词中每个字母的频率总值(例如,我将 E 赋值为 1,T 赋值为 2,A 赋值为 3,O 赋值为 4),并识别得分最高的、最长的单词。这些将是您最好的Hangman单词。

但是,我的篇幅用完了,而且我最后一次检查时,我应该写的是 shell 脚本中不同的变量引用格式。我发誓,在下一篇专栏中,我会回到这一点。除非您(暗示,暗示)给我写一张带有谜题或脚本挑战的便条来解决。

Dave Taylor 自 1980 年首次登录 ARPAnet 以来就一直参与 UNIX。这意味着,是的,他快要到 30 年的里程碑了。您几乎可以在任何在线地方找到他,但从这里开始:www.DaveTaylorOnline.com

加载 Disqus 评论