神奇的管道一个单词排序器实现过程

来源:互联网 发布:盛科网络弯曲评论 编辑:程序博客网 时间:2024/05/16 09:55

声明:本文为学习了"shell脚本学习指南"一书之后的学习手记.

p { margin-bottom: 0.21cm; }td p { margin-bottom: 0cm; }a:link { }

问题:实现一个文本处理程序,找出n个出现最频繁的单词,将单词打印出来,并在输出结果的列表上加入它们出现的次数,按照由大到小排序。

 

下面给出一个给出完成这个任务在*NIX上可以进行的一些学习活动.

*nix中学习*nix。(*nix代指各类unix)(下面的方法最初来自 <<unix/linux编程实践教程>>)

方法: 1. 阅读联机帮助。

2. 搜索联机帮助

3. 阅读.h文件。

4从参阅文件(SEEALSO)得到启示

联机文档的使用如下:

也就是manman命令.结果如下:

banxi1988@banxi1988-desktop:~$man man

得到帮助信息如下图。


1-1man man命令信息。

使用man命令的时候,你应该还要知道man命令信息页的一些信息。如下:

由于手册页(manpage)是用less程序来看的(可以方便地使屏幕上翻和下翻),所以在手册页(manpage)里可以使用less的所有选项。手册页manpage在很少的空间里提供了很多的信息。

less中比较重要的功能键有:

[q]退出;

[Enter]一行行地下翻;

[Space]一页页地下翻;

[b]上翻一页;

[/]后跟一个字符串和[Enter]来查找字符串;

[n]发现上一次查找的下一个匹配。


man命令中有一个参数很有用如下:

-K,--global-apropos search for text in all pages


根据题目的功能要求,下面对一单词统计功能命令查找搜索如下:


banxi1988@banxi1988-desktop:~$man -k count word


原始搜索出来的信息是比较多的。但是我们上面介绍了手册页的各章节的命令的说明。

我们作为这些基本文本要求的命令,应该是用户命令在手册页的第一章节。

所以重点考虑第一章节的,把第一章节的找出来比较相符的如上。


下面给出man手册页各章节的内容的一些规律.


章节

描 述

1

用户命令的使用方法,可以使用的参数等

2

系统调用只有系统才能执行的函数

3

库调用大多是libc函数,如qsort3

4

设备和特殊文件

5

文件格式和约定,比如/etc/passwd及其他可读文件

6

游戏

7

宏命令包和约定

8

系统管理命令,多数只有root可以执行

9

内核程序

 

根据手册页的说明找出如下:

cksum(1) - checksum and count the bytes in a file

sum(1) - checksum and count the blocks in a file

wc(1) - print newline, word, and byte counts for eachfile

gnome-dictionary(1) - Look up words on dictionaries


因为看起来wc命令比较符合.所以再help一下wc命令的使用.如下:


banxi1988@banxi1988-desktop:~$wc --help

用法:wc[选项]...[文件]...

 或:wc[选项]...--files0-from=F

输出每个指定文件的行数、单词计数和字节数,如果指定了

多于一个文件,继续给出所有相关数据的总计。如果没有指定

文件,或者文件为"-",则从标准输入读取数据。

-c,--bytes输出字节数统计

-m,--chars输出字符数统计

-l,--lines输出行数统计

--files0-from=文件从指定文件读取以NUL终止的名称,如果该文件被

指定为"-"则从标准输入读文件名

-L,--max-line-length显示最长行的长度

-w,--words显示单词计数

--help显示此帮助信息并退出

--version显示版本信息并退出


请向bug-coreutils@gnu.org报告wc的错误

GNUcoreutils 项目主页:<http://www.gnu.org/software/coreutils/>

GNU软件一般性帮助:<http://www.gnu.org/gethelp/>

请向<http://translationproject.org/team/zh_CN.html>报告wc的翻译错误

banxi1988@banxi1988-desktop:~$


虽然我们现在学习到了很多了,但是上面的只是统计出来了单词数。

但是我们要的是每个单词的单词数统计结果。


现在到了我们发挥想法的时候了,如果这是一个C语言的题,我们应该如何解决。

首先我们可能会有一个数组应该是结构数组。假设总共有N个不同的单词,分别统计每个单词的个数。

所以需要扫描N遍吧。然后到数组进行排序。将想要的单词级数据输出。

暂时不考虑其它的,因为过早优化是万恶之源。我们先过搜索下,有没有现成的工具能够做这些事情。

首先一个问题是就是单词的匹配问题,在C语言我们可以使用strcmp。看下在unix系统下我们可以用什么。


banxi1988@banxi1988-desktop:~$man -k match string words

(在结果中发现了很多第三章节的,可见GNU/LINUX的编程还是很方便的,有众多的功能可用的函数。)把匹配的并且是第章节的列出如下:

egrep(1) - print lines matching a pattern

fgrep(1) - print lines matching a pattern

git-grep(1) - Print lines matching a pattern

grep(1) - print lines matching a pattern

rgrep(1) - print lines matching a pattern


上面的东西都有一个共同的点就是都有grep。百度之,或者Google之。

Grep的功能就知道了。


下面简单说明下:Unix实用工具来源于ed中的一个全局命令。

g/re/p

其中g代表globalRe代表正则表达式。

下面我们来看下系统的帮助文档是怎么说明的!

banxi1988@banxi1988-desktop:~$grep --help

用法:grep [选项]...模式[文件]...

在每个文件中查找样式或标准输入。

PATTERN默认的是一个基本的正则表达式(BRE)

例: grep-i 'hello world' menu.h main.c


正则表达式的选择和解释:

-E,--extended-regexp PATTERN是一个扩展的正则表达式(ERE)

-F,--fixed-strings PATTERN是一套新行分离修复字符串

-G,--basic-regexp PATTERN是一个基本的正则表达式(BRE)

-P,--perl-regexp PATTERN是一个Perl正则表达式

-e,--regexp=PATTERN 使用PATTERN来匹配

-f,--file=FILE FILE来获得 PATTERN

-i,--ignore-case 忽略大小写

-w,--word-regexp 强制PATTERN仅匹配整个词

-x,--line-regexp 强制PATTERN仅匹配整行

-z,--null-data 结尾为0字节而不是新行符的数据行


杂项:

-s,--no-messages不显示错误信息

-v,--invert-match选择不匹配的行

-V,--version打印版本信息并退出

--help显示本帮助并退出

--mmap如果可能,使用内存映象作为输入


输出控制:

-m,--max-count=NUM 在有NUM个匹配后停止

-b,--byte-offset 在输出行的同时打印字节位移

-n,--line-number 在输出行的同时打印行数

--line-buffered flush output on every line

-H,--with-filename print the filename for each match

-h,--no-filename suppress the prefixing filename on output

--label=LABEL print LABEL as filename for standard input

-o,--only-matching show only the part of a line matching PATTERN

-q,--quiet, --silent suppress all normal output

--binary-files=TYPE assume that binary files are TYPE;

TYPEis `binary', `text', or `without-match'

-a,--text 等同于--binary-files=text

-I 等同于--binary-files=without-match

-d,--directories=ACTION how to handle directories;

ACTIONis `read', `recurse', or `skip'

-D,--devices=ACTION how to handle devices, FIFOs and sockets;

ACTIONis `read' or `skip'

-R,-r, --recursive 等同于--directories=recurse

--include=FILE_PATTERN 只搜索符合FILE_PATTERN型式的文件

--exclude=FILE_PATTERN 跳过名字为FILE_PATTERN的文件或目录

--exclude-from=FILE skip files matching any file pattern from FILE

--exclude-dir=PATTERNdirectories that match PATTERN will be skipped.

-L,--files-without-match print only names of FILEs containing no match

-l,--files-with-matches print only names of FILEs containing matches

-c,--count print only a count of matching lines per FILE

-T,--initial-tab make tabs line up (if needed)

-Z,--null FILE名字后打印0字节


上下文控制:

-B,--before-context=NUM 打印NUM行上文

-A,--after-context=NUM 打印NUM行下文

-C,--context=NUM 打印NUM行输出上下文

-NUM --context=NUM相同

--color[=WHEN],

--colour[=WHEN] 使用标记来高亮匹配的字符串;

WHEN可取值为“always”,“never”或“auto”

-U,--binary 不去掉EOL(MSDOS)处的CR字符

-u,--unix-byte-offsets 如果CR不在那里(MSDOS),报告偏移值


egrep’与 ‘grep-E’含义一致。‘fgrep’与‘grep-F’的含义一致。

直接调用‘egrep’和‘fgrep’的方式已经被废弃了。

没有 FILE或者FILE-,读取标准输入。如果少于两个FILE

假设 -h。如果任意行被选中,退出状态为0

如果出现任何错误并且-q未被给出,退出状态为2


汇报错误到:bug-grep@gnu.org

GNUGrep 主页:<http://www.gnu.org/software/grep/>

GNU软件一般性帮助:<http://www.gnu.org/gethelp/>


上面的有几个选项正符合我们的意思。如下:

-i,--ignore-case 忽略大小写

-w,--word-regexp 强制PATTERN仅匹配整个词

-c,--count print only a count of matching lines per FILE


我们用我们的测试文本(testcat.txt)来测试一下。

banxi1988@banxi1988-desktop:~$cat testcat.txt

thisis a file to test cat command.

forthe purpose is write by banxi1988

at2010-01-16

banxi1988@banxi1988-desktop:~$


我们先来测试下,用grep来查找is这个单词吧。

如下

banxi1988@banxi1988-desktop:~$grep is testcat.txt

thisisa file to test cat command.

forthe purpose iswrite by banxi1988

banxi1988@banxi1988-desktop:~$


grep将匹配的都变成了红色,而且匹配了单词的一部分。但是我们要匹配整个单词。

所以用上面说明的选项。

如下:

banxi1988@banxi1988-desktop:~$grep -w -i is testcat.txt

thisisa file to test cat command.

forthe purpose iswrite by banxi1988

banxi1988@banxi1988-desktop:~$

现在好了,但是呢,没有打印出匹配的个数。只是表明在里面匹配有。

查看选项测试之如下:

在输出控制选项下找出下面这样一个。

-o,--only-matching show only the part of a line matching PATTERN


banxi1988@banxi1988-desktop:~$grep -w -o -i is testcat.txt

is

is

banxi1988@banxi1988-desktop:~$

我们又进 了一步了。但是还得打出多少来呢。

再次测试之。

banxi1988@banxi1988-desktop:~$grep -w -o -n -i 'is' testcat.txt

1:is

2:is

banxi1988@banxi1988-desktop:~$

上面加了选项n,表示输出行数。注意到没有我加了引号给单词。但结果是一样的,因为grep能够查找字符串,所以加上引号自己好看些。


输出控制的另外一个选项,测试之:

-c,--count print only a count of matching lines per FILE

banxi1988@banxi1988-desktop:~$grep -w -o -c -i 'is' testcat.txt

2

banxi1988@banxi1988-desktop:~$

但是这个选项是打印出匹配的行数,但是我一行就有几个单词呢?会怎么样?

testcat.txt改成下面的后测试之。

banxi1988@banxi1988-desktop:~$cat testcat.txt

thisis a file to test cat command. and this is is a good day?

forthe purpose is write by banxi1988

at2010-01-16


banxi1988@banxi1988-desktop:~$grep -w -o -c -i 'is' testcat.txt

2

banxi1988@banxi1988-desktop:~$

它还是打印出现行,所以不行啦。。

banxi1988@banxi1988-desktop:~$grep -w -o -i is testcat.txt

is

is

banxi1988@banxi1988-desktop:~$


现在我们倒回去一点,我们对每一个不同的单词这样来一遍。再来计算它的行数,然后再比较,虽然这个方法比较笨但还是比较可行的。然后但是上面的是我们自己一个一个的来找单词的。然后听说unix的批处理很强大,所以我们还是再想想。再多加一层。

首先我们把应该让单词作为文件输出还不是我们自己查看文件之后再找出来,所以我们得让它自动查找出每个单词然后计算次数。但是又有一个问题,就是,单词的重复计数,因为单词在前面出现放进行到后面就又会出现,然后再计算。

所以我们要找出另外一种方案来,其中之一就是,将已经遇到过的单词把它从文件中删除。

makestrs(1) - makes string table C source and header(s)

replace(1) - a string-replacement utility


找到两个看似有用的列出如上。查阅之,结果发现都不是我们想要的。于是我们还是回头吧。

上面一开始就有说明查看man手册页时从SEEALSO中找到灵感。现在我们就来试下吧。

mangrep

seealso如下:

SEEALSO

RegularManual Pages

awk(1), cmp(1), diff(1), find(1), gzip(1), perl(1), sed(1), sort(1),

xargs(1), zgrep(1), mmap(2), read(2), pcre(3), pcrepattern(3),

terminfo(5),glob(7), regex(7).

然后再查阅之。首先一个是awk

好跟我们的实验要求还有点联系,我们来help之。


由于页数的原因下面的过程不再重复.

通过反复的help maninfo 等帮助命令,得到解决方案(测试文档也一起)如下:

#!/bin/sh


#统计文章中的


#用在在英文文章中单词频率程序


#参数1文章的文件名.参数 2显示排在前N位单词.


#程序名.wf.sh


#用法:


#banxi1988@banxi1988-desktop:~$chmod u+x wf.sh


#banxi1988@banxi1988-desktop:~$ls -l wf.sh


#-rwxr--r--1 banxi1988 banxi1988 1433 2011-01-17 22:35 wf.sh


#banxi1988@banxi1988-desktop:~$./wf.sh testcat.txt 10


#


cat "$1" |


tr-s '[:punct:][:blank:]' '[/n*]' | # 将标点及空格替换为换行符.-s 选项意思是-s,

#--squeeze-repe如果匹配于SET1的字符在输入序列中存在连续的重复,


#在替换时会被统一缩为一个字符的长度


tr'[:upper:]' '[:lower:]' | # 将大写转换为小写.此时下面的uniq-i选项可以不用.


sort| # 使用默认的选项进行排序.


uniq-c -i | # -c, --count在每行前加上表示相应行目出现次数的前缀编号-i忽略大小写


sort-n -k 1 -r | # -n -n, --numeric-sort根据字符串数值比较


# -k, --key=位置1[,位置2]在位置1开始一个key,在位置2终止(默认为行尾)


# -r, --reverse逆序输出排序结果


head -n "$2" # head -n, --lines=[-]N显示每个文件的前N行内容;




#结果测试如下:


#banxi1988@banxi1988-desktop:~$cat testcat.txt


#this is a file to test cat command. and this is is a good day?


#forthe purpose is write by banxi1988


#at2010-01-16


#banxi1988@banxi1988-desktop:~$./wf.sh testcat.txt 5


# 4 is


# 2 this


# 2 a


# 1 write


# 1 to


#banxi1988@banxi1988-desktop:~$./wf.sh testcat.txt 1000


# 4 is


# 2 this


# 2 a


# 1 write


# 1 to


# 1 the


# 1 test


# 1 purpose


# 1 good


# 1 for


# 1 file


# 1 day


# 1 command


# 1 cat


# 1 by


# 1 banxi1988


# 1 at


# 1 and


# 1 2010


# 1 16


# 1 01


# 1


#banxi1988@banxi1988-desktop:~$


下面help之上面的关键主角.tr tr的手册页有丰富的帮助信息,下面只给出它的help信息.

banxi1988@banxi1988-desktop:~$tr --help


用法:tr[选项]...SET1 [SET2]


从标准输入中替换、缩减和/或删除字符,并将结果写到标准输出。




-c,-C, --complement首先补足SET1


-d,--delete删除匹配SET1的内容,并不作替换


-s,--squeeze-repeats如果匹配于SET1的字符在输入序列中存在连续的


重复,在替换时会被统一缩为一个字符的长度


-t,--truncate-set1先将SET1的长度截为和SET2相等


--help显示此帮助信息并退出


--version显示版本信息并退出




SET是一组字符串,一般都可按照字面含义理解。解析序列如下:




/NNN八进制值为NNN的字符(13个数位)


//反斜杠


/a终端鸣响


/b退格


/f换页


/n换行


/r回车


/t水平制表符


/v垂直制表符


字符1-字符2从字符1到字符2的升序递增过程中经历的所有字符


[字符*]SET2中适用,指定字符会被连续复制直到吻合设置1的长度


[字符*次数]对字符执行指定次数的复制,若次数以0开头则被视为八进制数


[:alnum:]所有的字母和数字


[:alpha:]所有的字母


[:blank:]所有呈水平排列的空白字符


[:cntrl:]所有的控制字符


[:digit:]所有的数字


[:graph:]所有的可打印字符,不包括空格


[:lower:]所有的小写字母


[:print:]所有的可打印字符,包括空格


[:punct:]所有的标点字符


[:space:]所有呈水平或垂直排列的空白字符


[:upper:]所有的大写字母


[:xdigit:]所有的十六进制数


[=字符=]所有和指定字符相等的字符




仅在SET1SET2都给出,同时没有-d选项的时候才会进行替换。


仅在替换时才可能用到-t选项。如果需要SET2将被通过在末尾添加原来的末字符的方式


补充到同SET1等长。SET2中多余的字符将被省略。只有[:lower:][:upper:]


以升序展开字符;在用于替换时的SET2中以成对表示大小写转换。-s作用于SET1,既不


替换也不删除,否则在替换或展开后使用SET2缩减。

原创粉丝点击