Shell 实践 - 单词和字母计数更有趣
如果您还记得一个月前,您会想起我收到了一封来自某人(暗示,暗示)的祝福邮件,询问
亲爱的 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中按什么顺序猜测字母了,如果仅此而已。
在我们结束之前,让我们回顾一下语料库中的单词,只找到那些至少十个字母长且不经常出现的单词。这是我将如何做到的
$ 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。