【智慧师傅2月2日】 c,搜索基础知识(2)linux和正则表达式的入门(1)

lvjunwen2009 2010-02-02 10:45:03
由于今天白天朋友来访,所以今天没有跟师傅学太多东西,今天晚上还是学了c的一些基本知识,还学了一点linux的命令,这些都是很浅的东西,还看了一下搜索的基本知识,另外师傅还教了我一些关于正则表达式的基本知识,如果大家没看过后两个方面的,可以来看看。由于我什么基础都没有,所以只能把师傅的有些话记下来,以后可能会看懂。
我的上一个帖子在http://topic.csdn.net/u/20100201/22/3574e5c9-edbb-456f-a45c-a25c16e06700.html?26668

今天师傅的一些话:
查询的时候,准确率,基本都是靠分词来达到吗?还有没有其它的方式,比如:搜索的几种策略来提高覆盖率和准确率?
准确率 可以通过对文档加以权重来实现
我们实现了实时索引,分索引库就行了,搜索多个索引库,小库可以随时索引,大库定时跟小库合并,如果处于性能考虑 可以将小库放到内存里
师傅教导我的话:实践是出真知,孔子说过 学而不思则罔 思而不学则殆
所以以后我要多多实践了。

第一部分:Linux的入门(—)
1.linux快速入门
Linux文件系统 windows是以盘符为基础的,而且每一个目录都是与相应的分区对应,linux是以文件树为基础,且它的所有的文件或者外部设备都是以文件的形式挂接在这棵文件树上面
Linux中把每一个分区和某一个目录对应,以后在对这个目录的操作就是对这个分区的操作,这样就实现了硬件管理手段和软件目录管理手段的统一,把分区和目录对应的过程就叫着挂载(mount),而这个挂载在文件树中的位置就是挂载点。
硬件分区是针对一个硬盘进行操作的,它可以分为:主分区,扩展分区,逻辑分区。
C盘 - /dev/hda1(主分区) D盘 - /dev/hda5(逻辑分区) E盘 - /dev/hda6(逻辑分区)
Swap交换分区
分区格式:windows 为 fat32. ntfs
Linux主要是 ext2,后来是ext3 支持很多分区格式
GRUB引导装入器 (类似嵌入式中bootloader),它负责装入内核并引导linux系统,位于硬盘开始部分。
(双系统是用linux的GRUB来引导装入器来启动windows或者linux)
Root权限 默认的主目录是/root下,普通用户在/home下
Linux的主要的文件类型分为四个部分:普通文件,目录文件,链接文件,设备文件
目录也是文件,他包含文件名和子目录名以及指向那些文件和子目录的指针。把文件和目录相对应起来,就构成了目录文件。在linux中每个文件都有一个唯一的数值,叫索引节点。索引节点存储在索引节点表(inode table)中。索引节点包含文件的所有信息。
链接文件类似与“快捷方式”
设备文件一般放在/dev这个目录下,它包括:块设备文件,字符设备文件
前者主要是数据的读写,后者主要是串行端口的接口设备

文件属性:可读(r),可写(w),可执行(x),文件有三种不同的用户组别:文件拥有者(u)
所属的用户组(g),系统的其它用户(o)
如果没有权限一般用_来表示
Linux把fat文件系统都称为vfat文件系统
NFS文件系统:网络文件系统

Linux的目录结构:主要是熟悉每个目录下有些什么
/bin和/usr/bin /boot /dev /etc /etc/rc.d /etc/rc.d/init
/home /lib /lost+found /media /misc /mnt /proc /root
/sbin /tmp /usr /usr/bin /usr/sbin /usr/src /srv /var
/sys

2 .linux的常用的命令(这里就不分析每个命令了,我把这些命令都试了一下)
Shell环境:linux的命令行界面
Shell是命令语言,命令解释程序以及程序设计语言的统称
Shell是一个解释性的程序设计语言、
用户切换:su - root
.(环境标量:用户运行环境的参数集合,linux多用户的,每一个用户登录系统后,都会有一个专用的运行环境。通常每个用户默认的环境都是相同的,而这个默认环境实际上就是一组环境变量的定义)
用户管理(useradd和passwd) 系统管理命令(ps和kill)
(管道的概念:就是把一个程序的输出直接连接到另一个程序的输入,而不经过任何中间文件,shell中字符“|”表示管道线)
磁盘相关命令(fdisk) free,df,du,fdisk
磁盘挂载命令(mount)mount –t vfat /dev/sda1 /mnt/u
改变工作目录 cd /home/lvjunwen2009/ ./表示当前目录 ../代表上级目录
Ls 列出目录的内容
Ls –l 显示格式:文件类型与权限 链接数 文件属主 文件大小 修改的时间 名字
Mkdir 创建一个目录 –m ,-p可以用-p一次创建多级目录
Mkdir –m 777 ./lvjunwen2009
cat 连接并显示指定的一个或者多个文件的有关信息 –n ,-b
cp mv rm三个命令 -i询问是否将存在的目标文件覆盖 -f禁止交互操作
注意cp中-a选项 ,和rm中的-r选项
Chown和chgrp命令 chown修改文件的所有者和组别 chgrp修改文件的组的所有权
Chmod改变文件的访问权限 注意:u,o,g,a四个级别,用“+”表示增加权限,用“-“减少权限 chmod a+rx,u+w lvjunwen2009.tgz
文件中搜索特定的内容,并将含有的行标准输出
Less分页器grep “h” ./-r |less其中-r为指定目录
在linux中,正则表达式通常用来查找文本的模式,以及对文本执行“搜索-替换“操作等等
正则表达式我专门做了一下总结,在第四部分。
在指定目录中搜索文件,find ./ -name qiong.c
Find的选项-depth,-mount ,-name,-user
Find支持混合查找,例子:find /etc –size +50000c –and –mtime +1
Locate 用于查找文件先建立一个包括系统内所有文件名称以及路径的数据库,之后当寻找时候只需查询这个数据库,不用实际深入档案之中了。比find快
Ln作用是为某一个文件在另外一个位置建立一个符号链接。当需要在不同的目录中用到相同的文件时候
Ln分为软链接和硬链接两种,ln –s ** **为软链接,ln ** **为硬链接。


第二部分:搜索的基本知识(2):
二、网络蜘蛛
概述
蜘蛛(即Web Spider),实际上是一个基于HTTP协议的网络应用程序。网络蜘蛛是通过网页的链接地址来寻找网页,从网站某一个页面(通常是首页)开始,读取网页的内容,并抽取出网页中的其它超链接地址,然后通过这些链接地址寻找下一个网页,这样一直循环下去,直到把这个网站所有的网页都抓取完为止。
在抓取网页的时候,网络蜘蛛一般有两种策略:广度优先和深度优先。广度优先是指网络蜘蛛会先抓取起始网页中链接的所有网页,然后再选择其中的一个链接网页,继续抓取在此网页中链接的所有网页。这是最常用的方式,因为这个方法可以让网络蜘蛛并行处理,提高其抓取速度。深度优先是指网络蜘蛛会从起始页开始,一个链接一个链接跟踪下去,处理完这条线路之后再转入下一个起始页,继续跟踪链接。这个方法有个优点是网络蜘蛛在设计的时候比较容易。
主要组成
根据抓取过程蜘蛛主要分为三个功能模块,一个是网页读取模块主要是用来读取远程Web服务器上的网页内容,另一个是超链分析模块,这个模块主要是分析网页中的超链接,将网页上的所有超链接提取出来,放入到待抓取URL列表中,再一个模块就是内容分析模块,这个模块主要是对网页内容进行分析,将网页中所有超标志去掉只留下网页文字内容。蜘蛛的主要工作流程如下图所示:
首先蜘蛛读取抓取站点的URL列表,取出一个站点URL,将其放入未访问的URL列表(UVURL列表)中,如果UVURL不为空刚从中取出一个URL判断是否已经访问过,若没有访问过则读取此网页,并进行超链分析及内容分析,并将些页存入文档数据库,并将些URL放入已访问URL列表(VURL列表),直到UVRL为空为止,此时再抓取其他站点,依次循环直到所有的站点URL列表都抓取完为止。
...全文
6471 17 打赏 收藏 转发到动态 举报
写回复
用AI写文章
17 条回复
切换为时间正序
请发表友善的回复…
发表回复
huanmie_09 2010-02-03
  • 打赏
  • 举报
回复
强悍。
bobo364 2010-02-03
  • 打赏
  • 举报
回复
jf
lvjunwen2009 2010-02-03
  • 打赏
  • 举报
回复
[Quote=引用 13 楼 adaifire 的回复:]
智慧师傅是本书吗?
[/Quote]
是一个师傅,他指导我怎么学习的那种啦。
lvjunwen2009 2010-02-03
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 lovesi3344 的回复:]


师傅是面授的还是函授的??
[/Quote]
师傅是函授啦。
WPooh 2010-02-02
  • 打赏
  • 举报
回复
...学习...
adaifire 2010-02-02
  • 打赏
  • 举报
回复
智慧师傅是本书吗?
camelisi 2010-02-02
  • 打赏
  • 举报
回复
LZ没学过编程,第2天能记到这么多更强...
camelisi 2010-02-02
  • 打赏
  • 举报
回复
师傅真强
XTIAQGNUI 2010-02-02
  • 打赏
  • 举报
回复
师傅真好。
xylicon 2010-02-02
  • 打赏
  • 举报
回复
智慧师傅是谁呢?
lvjunwen2009 2010-02-02
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 dontkissbossass 的回复:]
强,正则一天就讲完。那么master regular express 要看N天呢
[/Quote]
正则还有一些!以前没看过的东西,很兴奋啊!我明天把正则第二部分贴出来。
DontKissBossAss 2010-02-02
  • 打赏
  • 举报
回复
我也想那要那师傅了。。。。铅哥,也教教咱们呗
DontKissBossAss 2010-02-02
  • 打赏
  • 举报
回复
那么 ->那本
DontKissBossAss 2010-02-02
  • 打赏
  • 举报
回复
强,正则一天就讲完。那么master regular express 要看N天呢
lovesi3344 2010-02-02
  • 打赏
  • 举报
回复



师傅是面授的还是函授的??
lvjunwen2009 2010-02-02
  • 打赏
  • 举报
回复
typedef struct CodeIndexNode{
char KeyValue[17];
int nIndex;
}CodeIndex;

//根据词语字数建立的一级索引结点
typedef struct WordsIndexNode{
int nKeyCount;
int nKeyBeginIndex;
int nKeyEndIndex;
int CodeIndexCount;
CArray<CodeIndexNode*,CodeIndexNode*>HexIndex;
}WordsIndex;

//关键字结点
typedef struct KeyWordNode{
CString strKeyWord; //关键字
int nID; //关键字ID
}
2、 切分方法
由于采用的是基于词库匹配的正向最大匹配算法(通常简称为MM法),其基本思想为:设D为词典,MAX表示D中的最大词长,str为待切分的字串。MM法是每次从str中取长度为MAX的子串与D中的词进行匹配。若成功,则该子串为词,指针后移MAX个汉字后继续匹配,否则子串逐次减一进行匹配。

1)、读取词库,并读取相应的静态索引,建立词库上的索引;
2)、读取待切分的字串str;
3)、从待切分字串str中取出一个长度为MAX的子串sstr,到词典中去匹配,若匹配成功则取下一个长度为MAX的子串进行匹配,否则将子串sstr从后面截去一个字后继续匹配,直到匹配成功或者子串sstr中只有一个字为止。若匹配成功则从匹配成功的词的位置开始再截取下一长度为MAX的子串sstr进行匹配,依次循环直到将str串匹配完为止。
4)匹配过程:首先根据子串sstr的长度(这里指的是字数)确定一级索引也就是确定分组,这个过程可以二分查找法,也可以采用Hash函数直接定位,但是由于分组数很少(不会超过20)因而两种方法没有多大的区别。在确定了分组后再根据首汉字的内码确定二级索引,因为二级索引是按内码从小到大的顺序因而可采用拆半查找方法,找到以后再确定三级索引,这样将进行匹配的过程缩小到一个很小的范围,在这个范围内匹配不成功则进行下一个串的匹配。通过确定三级索引确定了进行匹配的最小词条集。
3、 切分结果的保存(也就是顺排档数据的保存)
由于数据量很大,不能全存放在内存中,所以每处理完一文档就将其切分结果存放到外部文件中,这里可以借助其它关系型数据库,这有利于于索引器导出数据将其导成倒排档索引文件。主要用到的结构体定义如下:
//Hit结点
typedef struct HitNode{
int nPos;//位置
HitNode* pNext;//下一hit指针
};
//文档列表结点
typedef struct DocListNode{
_int64 nDocID;//DOC ID
int nHits;//词出现的次数
float fWeight;//词在文中的要重
HitNode* pHitHead;//Hit链表头指针
DocListNode* pNext;
};
//一级索引结点
typedef struct LexIconNode{
int nKeyWordID;//关键字ID
int nDocs;//文档数
DocListNode* pDocListHead;//文档链表头指针
LexIconNode* pNext;
};
在数据库中存放的字段主要有:DocID、WordID、Hit(位置)、Weight(权值)。这样索引器导出时将会按WordID和权值进行排序。
经验总结
1、 存在的问题
1)、在词库组织方面采用静态的索引不利于于词库的变化,若有一新词出现,则需要重建整个词库的索引,因为下标都发生了变量。这不利于词库的扩充。
2)、词的ID分配机制也有些不足,这里的ID都是跟词的内码相关,也就是跟词在词库中的排序顺序有关,这样若有新词添加进来后,就会改变其后面所有词的ID。
3)、切词的速度不够快,虽然每秒能达到400多字,但是词库比较小,只有5万多的词条。若司库很大时速度会有所下降。
4)、因为汉字是双字节表示的,所以在切分之前转换成Unicode,转换成多字节表示,经过测试发现,多字节转换占用了很大一块CPU时间,将近占去了40%的时间。
5)、在进行多字节转换时开设的缓冲区为1000个汉字,若需要转换的汉字多于1000则会出错,但若开设的缓冲区过大是对系统资源的浪费。
6)、是一种机械的切词方法,没有对歧义词进行排除和分析。
7)、没有新词的识别功能,也就是不通过词典不能识别切分出新的词。
2、 改进的方向
1)、词表组织
在词表上添加三级索引是一个较好的方法,但是采用静态的索引不利于词库的扩充,因而词库的索引可动态生成,在读取词库的同时构建索引,但是这种方法对于查询器会产生一个不利影响,因为查询器也需要读取词库而构建索引的过程比较费时间,这样用户查询时会有一定的延时,所以应根据需要采用一种折衷的办法。
整个切词过程实际就是在词表上的查找过程,而词表相当于就是一个查找表,所以这个查找表的组织相当关键,它决定切分的速度。所以在这个查找表上必须添加索引来加快速度,本人觉得可以根据各词的前两个汉字的内码建立一个Hash函数索引,这样即不需要分组也不需要用二分法来查找,直接定位肯定会减少进行匹配的次数。但需要解决冲突问题,因为有的词属于其他词的前向子串。
2)、词条ID分配
词条ID(WordId)可按入库的先后顺序递增,不管其内码。但是入库后应该按其内码插入到适当的位置。但是在建立倒排档索引数据时应该按WordID从小到大排序,这样对于查询器可以根据WordID用Hash函数直接定位到相应的读取位置。
3)、多字节转换问题
多字节转换需要将所有的待切分串都转换成多字节表示,这个过程相当费时,若不需要转换直接切分则会大大提高速度,对于纯汉字的字串可以这样做,但是有中英文混合的字符串就不太适合,因为英文字符只占一个字节表示,这样会出现将一个当字从中间切开。这样字符串移位时先判断其是否是Ansi码,若是则移一个字节,若不是则移两个字节。
4)、歧义识别
5)、新词识别
新词的识别,可以按词频统计的方法,若某一字串出现的次数高于某一频率时可以认为一个词,将其切分出来。

第三部分:正则表达式(1)

概述:
在编写处理字符串的程序或网页时,经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说,正则表达式就是记录文本规则的代码。
很可能你使用过Windows/Dos下用于文件查找的通配符(wildcard),也就是*和?。如果你想查找某个目录下的所有的Word文档的话,你会搜索*.doc。在这里,*会被解释成任意的字符串。和通配符类似,正则表达式也是用来进行文本匹配的工具,只不过比起通配符,它能更精确地描述你的需求——当然,代价就是更复杂

入门

假设你在一篇英文小说里查找hi,你可以使用正则表达式hi。这几乎是最简单的正则表达式了,它可以精确匹配这样的字符串:由两个字符组成,前一个字符是h,后一个是i。通常,处理正则表达式的工具会提供一个忽略大小写的选项,如果选中了这个选项,它可以匹配hi,HI,Hi,hI这四种情况中的任意一种。不幸的是,很多单词里包含hi这两个连续的字符,比如him,history,high等等。用hi来查找的话,这里边的hi也会被找出来。如果要精确地查找hi这个单词的话,我们应该使用\bhi\b。\b是正则表达式规定的一个特殊代码(好吧,某些人叫它元字符,metacharacter),代表着单词的开头或结尾,也就是单词的分界处。虽然通常英文的单词是由空格,标点符号或者换行来分隔的,但是\b并不匹配这些单词分隔字符中的任何一个,它只匹配一个位置。如果需要更精确的说法,\b匹配这样的位置:它的前一个字符和后一个字符不全是(一个是,一个不是或不存在)\w。假如你要找的是hi后面不远处跟着一个Lucy,你应该用\bhi\b.*\bLucy\b。这里,.是另一个元字符,匹配除了换行符以外的任意字符。*同样是元字符,不过它代表的不是字符,也不是位置,而是数量——它指定*前边的内容可以连续重复使用任意次以使整个表达式得到匹配。因此,.*连在一起就意味着任意数量的不包含换行的字符。现在\bhi\b.*\bLucy\b的意思就很明显了:先是一个单词hi,然后是任意个任意字符(但不能是换行),最后是Lucy这个单词。换行符就是'\n',ASCII编码为10(十六进制0x0A)的字符。如果同时使用其它元字符,我们就能构造出功能更强大的正则表达式。比如下面这个例子:0\d\d-\d\d\d\d\d\d\d\d匹配这样的字符串:以0开头,然后是两个数字,然后是一个连字号“-”,最后是8个数字(也就是中国的电话号码。当然,这个例子只能匹配区号为3位的情形)。这里的\d是个新的元字符,匹配一位数字(0,或1,或2,或……)。-不是元字符,只匹配它本身——连字符(或者减号,或者中横线,或者随你怎么称呼它)。为了避免那么多烦人的重复,我们也可以这样写这个表达式:0\d{2}-\d{8}。 这里\d后面的{2}({8})的意思是前面\d必须连续重复匹配2次(8次)。

测试正则表达式
http://regexpal.com/

元字符
现在你已经知道几个很有用的元字符了,如\b,.,*,还有\d.正则表达式里还有更多的元字符,比如\s匹配任意的空白符,包括空格,制表符(Tab),换行符,中文全角空格等。\w匹配字母或数字或下划线或汉字等。对中文/汉字的特殊处理是由.Net提供的正则表达式引擎支持的,其它环境下的具体情况请查看相关文档。
下面来看看更多的例子:
\ba\w*\b匹配以字母a开头的单词——先是某个单词开始处(\b),然后是字母a,然后是任意数量的字母或数字(\w*),最后是单词结束处(\b)。
好吧,现在我们说说正则表达式里的单词是什么意思吧:就是不少于一个的连续的\w。不错,这与学习英文时要背的成千上万个同名的东西的确关系不大 :)\d+匹配1个或更多连续的数字。这里的+是和*类似的元字符,不同的是*匹配重复任意次(可能是0次),而+则匹配重复1次或更多次。\b\w{6}\b 匹配刚好6个字符的单词。
元字符^(和数字6在同一个键位上的符号)和$都匹配一个位置,这和\b有点类似。^匹配你要用来查找的字符串的开头,$匹配结尾。这两个代码在验证输入的内容时非常有用,比如一个网站如果要求你填写的QQ号必须为5位到12位数字时,可以使用:^\d{5,12}$。这里的{5,12}和前面介绍过的{2}是类似的,只不过{2}匹配只能不多不少重复2次,{5,12}则是重复的次数不能少于5次,不能多于12次,否则都不匹配。因为使用了^和$,所以输入的整个字符串都要用来和\d{5,12}来匹配,也就是说整个输入必须是5到12个数字,因此如果输入的QQ号能匹配这个正则表达式的话,那就符合要求了。和忽略大小写的选项类似,有些正则表达式处理工具还有一个处理多行的选项。如果选中了这个选项,^和$的意义就变成了匹配行的开始处和结束处。

字符转义
如果你想查找元字符本身的话,比如你查找.,或者*,就出现了问题:你没办法指定它们,因为它们会被解释成别的意思。这时你就得使用\来取消这些字符的特殊意义。因此,你应该使用\.和\*。当然,要查找\本身,你也得用\\.
例如:C:\\Windows匹配C:\Windows。

重复
* 重复零次或更多次
+ 重复一次或更多次
? 重复零次或一次
{n} 重复n次
{n,} 重复n次或更多次
{n,m} 重复n到m次

Windows\d+匹配Windows后面跟1个或更多数字
^\w+匹配一行的第一个单词(或整个字符串的第一个单词,具体匹配哪个意思得看选项设置)

字符类
要想查找数字,字母,空白是很简单的,因为已经有了对应这些字符集合的元字符,但是如果你想匹配没有预定义元字符的字符集合(比如元音字母a,e,i,o,u),应该怎么办?很简单,你只需要在方括号里列出它们就行了,像[aeiou]就匹配任何一个英文元音字母,[.?!]匹配标点符号(.或?或!)。我们也可以轻松地指定一个字符范围,像[0-9]代表的含意与\d就是完全一致的:一位数字;同理[a-z0-9A-Z_]也完全等同于\w(如果只考虑英文的话)。
下面是一个更复杂的表达式:\(?0\d{2}[) -]?\d{8}。
“(”和“)”也是元字符,后面的分组节里会提到,所以在这里需要使用转义。
这个表达式可以匹配几种格式的电话号码,像(010)88886666,或022-22334455,或02912345678等。我们对它进行一些分析吧:首先是一个转义字符\(,它能出现0次或1次(?),然后是一个0,后面跟着2个数字(\d{2}),然后是)或-或空格中的一个,它出现1次或不出现(?),最后是8个数字(\d{8})。

嗯,还有一些正则表达式的奇妙的用,只能明天贴出来了,因为实在是看不完了。

第四部分:c语言部分(2)

1.数组:
方括号 Bracket
c编译器并不检查count[-1]或者越界错误
遍历:Traversal:将数组中的每一个元素都访问了一遍。
数组不能相互赋值,也不能将数组作为函数的参数或者返回值
数组类型作为右值使用的时候,自动的转换程指向数组首元素的指针

计算机生成完全随机数(psevdorandom)
rand()函数 在stdlib类库中,返回0到RAND之间均匀的分布整数
预处理做的两件事情:1.把头文件stdio.h和stdlib.h在代码中展示
二是把#define定义的标识符替换
#为预处理提示(prepocessing directive)
宏(macro)定义:不仅定义了常量,还定义了更加复杂的语句结构

硬编码(hard coding)

字符串可以看着是一个数组 char c="hello ,world.\n"[0]
不能通过下标来修改其中的字符,做右值的时候自动地换成指向首元素的指针,printf会从数组str的开头一直打印到“\0"字符为止,如果不够的话会很危险
c语言的存储方式为:row_major方式


2.c的编码风格:
(1). 缩进和空白 a、关键字if, while, for与其后的控制表达式的(括号之间插入一个空格分隔,但括号内的表达式
应紧贴括号。例如:
while (1);
b、双目运算符的两侧插入一个空格分隔,单目运算符和操作数之间不加空格,例
如i = i + 1、++i、!(i < 1)、-x、&a[1]等。
c、后缀运算符和操作数之间也不加空格,例如取结构体成员s.a、函数调用foo(arg1)、取数组
成员a[i]。
d、,号和;号之后要加空格,这是英文的书写习惯,例
如for (i = 1; i < 10; i++)、foo(arg1, arg2)。
e、以上关于双目运算符和后缀运算符的规则不是严格要求,有时候为了突出优先级也可以写得
更紧凑一些,例如for (i=1; i<10; i++)、distance = sqrt(x*x + y*y)等。但是省略的
空格一定不要误导了读代码的人,例如a||b && c很容易让人理解成错误的优先级。
f、由于标准的Linux终端是24行80列的,接近或大于80个字符的较长语句要折行写,折行后用
空格和上面的表达式或参数对齐,例如:

if (sqrt(x*x + y*y) > 5.0
&& x < 0.0
&& y > 0.0)

再比如:

foo(sqrt(x*x + y*y),
a[i-1] + b[i-1] + c[i-1])
h、较长的字符串可以断成多个字符串然后分行书写,例如:
printf("This is such a long sentence that "
"it cannot be held within a line\n"); C编译器会自动把相邻的多个字符串接在一起,以上两个字符串相当于一个字符串"This is such a long sentence that it cannot be held within a line\n"。
g、有的人喜欢在变量定义语句中用Tab字符,使变量名对齐,这样看起来也很好,但不是严格
要求的。

(2). 标识符命名
标识符命名应遵循以下原则:

a. 标识符的命名要清晰明了,可以使用完整的单词和大家易于理解的缩写。短的单词可以通
过去元音形成缩写,较长的单词可以取单词的头几个字母形成缩写,也可以采用大家基本
认同的缩写。例如count写成cnt ,block写成blk ,length写成len ,window写
成win ,message写成msg ,temporary可以写成temp ,也可以进一步写成tmp。
b. 内核风格规定变量、函数和类型采用全小写加下划线的方式命名,常量(宏定义和枚举常
量)采用全大写加下划线的方式命名。上面举例的函数名radix_tree_insert、类型
名struct radix_tree_root、常量名RADIX_TREE_MAP_SHIFT等。
c. 全局变量和全局函数的命名一定要详细,不惜多用几个单词多写几个下划线,例如函数
名radix_tree_insert,因为它们在整个项目的许多源文件中都会用到,必须让使用者明确
这个变量或函数是干什么用的。局部变量和只在一个源文件中调用的内部函数的命名可以
简略一些,但不能太短,不要使用单个字母做变量名,只有一个例外:用i、j 、k做循环变
量是可以的。
d. 针对中国程序员的一条特别规定:禁止用汉语拼音作为标识符名称,可读性极差。

(3). 函数

每个函数都应该设计得尽可能简单,简单的函数才容易维护。应遵循以下原则:

a. 实现一个函数只是为了做好一件事情,不要把函数设计成用途广泛、面面俱到的,这样的
函数肯定会超长,而且往往不可重用,维护困难。
b. 函数内部的缩进层次不宜过多,一般以少于4层为宜。如果缩进层次太多就说明设计得太
复杂了,应该考虑分割成更小的函数来调用
c. 函数不要写得太长,建议在24行的标准终端上不超过两屏,太长会造成阅读困难,如果一
个函数超过两屏就应该考虑分割函数了。[CodingStyle] 中特别说明,如果一个函数在概念
上是简单的,只是长度很长,这倒没关系。例如函数由一个大的switch组成,其中有非常
多的case,这是可以的,因为各个case之间互不影响,整个函数的复杂度只等于其中一
个case的复杂度,这种情况很常见,例如TCP协议的状态机实现。
d. 执行函数就是执行一个动作,函数名通常应包含动词,例
如get_current、radix_tree_insert。
e. 比较重要的函数定义上面必须加注释,说此函数的功能、参数、返回值、错误码等。
f. 另一种度量函数复杂度的办法是看有多少个局部变量,5到10个局部变量就已经很多了,
局部变量再多就很难维护了,应该考虑分割函数。
lvjunwen2009 2010-02-02
  • 打赏
  • 举报
回复
关键技术
1、 多线程技术:由于抓取的站点URL相当多,采用单线程蜘蛛抓取时速度不够,也不能满足实际的需要。因而需要多线程技术来创建多个蜘蛛线程来同时抓取,以提高速度。
2、 网页抓取:网页抓取是基于HTTP协议之上的,网页上的资源有多种,有网页,有Word文档也有其他类型的文件,这样抓取时需要判断URL所指向资源的类型。
3、 超链分析:超链分析是一个比较重要的环节,需要对HTML的各种标志(tag)有一个很全面的了解。需要反复测试,考虑各种情形的发生。
超链分析时从网页里提取出来的是相对于当前页的相对URL,因而需要根据当前页的绝对URL将提取的这个URL转换成绝对URL。在此过程中需要根据ParentURL(就是当前页的URL)作出各种判断。


经验总结
商业化的蜘蛛需要抓取上亿的网页,因而抓取速度是一个关键,另外蜘蛛需要自动运行,尽是减少人工的参与,因而系统的性能也是一个很重要的关键,系统能够在发生异常的时候自动进行处理,防止程序的退出和死机。本人认为有一些细节需要注意:
1、 系统应该使用多线程,使用多个蜘蛛同时抓取,在可能的情况下,最好是做成分布式的蜘蛛程序,蜘蛛应该分布地网络上多台服务器上协同抓取网页,这样速度会更快,更符合我们的实际应用。
2、 对于同一网站的网页应该采用同一个HttpConnection这样有效地节省创建一个连接的时间,另外对于抓取的URL采用域名缓冲机制(可在网关一级上实现),这样抓取时减少由域名到IP地址的转换时间以及重复的域名转换。若能做到这一步将会大大减少抓取时间,因为访问一URL时每次都要进行域名到主机IP地址的转换。
3、 最好是能够将读取网页、超链分析及网页内容分析三部分分开来做,让它们并行协同工作,这样效率会更高。因为在这三个过程中网页读取比起其他两个功能来说是一个长任务,最耗时间。当抓取完一网页后,在抓取下一网页的时候让去执行超链分析和内容分析。这样在下一网页抓取完成之前超链分析和内容分析任务就能完成,抓取任务不会延迟,这样节省了一些时间。


三、切词器
概述
1、 概述
众所周知,英文是以词为单位的,词和词之间是靠空格隔开,而中文是以字为单位,句子中所有的字连起来才能描述一个意思。例如,英文句子I am a student,用中文则为:“我是一个学生”。计算机可以很简单通过空格知道student是一个单词,但是不能很容易明白“学”、“生”两个字合起来才表示一个词。把中文的汉字序列切分成有意义的词,就是中文分词,有些人也称为切词。我是一个学生,分词的结果是:我 是 一个 学生。
2、切词算法
现有的分词算法可分为三大类:基于字符串匹配的分词方法、基于理解的分词方法和基于统计的分词方法。
  1)、基于字符串匹配的分词方法
  这种方法又叫做机械分词方法,它是按照一定的策略将待分析的汉字串与一个“充分大的”机器词典中的词条进行匹配,若在词典中找到某个字符串,则匹配成功(识别出一个词)。按照扫描方向的不同,串匹配分词方法可以分为正向匹配和逆向匹配;按照不同长度优先匹配的情况,可以分为最大(最长)匹配和最小(最短)匹配;按照是否与词性标注过程相结合,又可以分为单纯分词方法和分词与标注相结合的一体化方法。常用的几种机械分词方法如下:
  a)正向最大匹配法(由左到右的方向);
  b)逆向最大匹配法(由右到左的方向);
  c)最少切分(使每一句中切出的词数最小)。
  还可以将上述各种方法相互组合,例如,可以将正向最大匹配方法和逆向最大匹配方法结合起来构成双向匹配法。由于汉语单字成词的特点,正向最小匹配和逆向最小匹配一般很少使用。一般说来,逆向匹配的切分精度略高于正向匹配,遇到的歧义现象也较少。统计结果表明,单纯使用正向最大匹配的错误率为1/169,单纯使用逆向最大匹配的错误率为1/245。但这种精度还远远不能满足实际的需要。实际使用的分词系统,都是把机械分词作为一种初分手段,还需通过利用各种其它的语言信息来进一步提高切分的准确率。
  一种方法是改进扫描方式,称为特征扫描或标志切分,优先在待分析字符串中识别和切分出一些带有明显特征的词,以这些词作为断点,可将原字符串分为较小的串再来进行机械分词,从而减少匹配的错误率。另一种方法是将分词和词类标注结合起来,利用丰富的词类信息对分词决策提供帮助,并且在标注过程中又反过来对分词结果进行检验、调整,从而极大地提高切分的准确率。
  2)、基于理解的分词方法
  这种分词方法是通过让计算机模拟人对句子的理解,达到识别词的效果,但这种方法需要大量的词法、句法、语义知识。其基本思想就是在分词的同时进行句法、语义分析,利用句法信息和语义信息来处理歧义现象。它通常包括三个部分:分词子系统、句法语义子系统、总控部分。在总控部分的协调下,分词子系统可以获得有关词、句子等的句法和语义信息来对分词歧义进行判断,即它模拟了人对句子的理解过程。这种分词方法需要使用大量的语言知识和信息。由于汉语语言知识的笼统、复杂性,难以将各种语言信息组织成机器可直接读取的形式,因此目前基于理解的分词系统还处在试验阶段。
  3)、基于统计的分词方法
  从形式上看,词是稳定的字的组合,因此在上下文中,相邻的字同时出现的次数越多,就越有可能构成一个词。因此字与字相邻共现的频率或概率能够较好的反映成词的可信度。可以对语料中相邻共现的各个字的组合的频度进行统计,计算它们的互现信息。定义两个字的互现信息,计算两个汉字X、Y的相邻共现概率。互现信息体现了汉字之间结合关系的紧密程度。当紧密程度高于某一个阈值时,便可认为此字组可能构成了一个词。这种方法只需对语料中的字组频度进行统计,不需要切分词典,因而又叫做无词典分词法或统计取词方法。但这种方法也有一定的局限性,会经常抽出一些共现频度高、但并不是词的常用字组,例如“这一”、“之一”、“有的”、“我的”、“许多的”等,并且对常用词的识别精度差,时空开销大。实际应用的统计分词系统都要使用一部基本的分词词典(常用词词典)进行串匹配分词,同时使用统计方法识别一些新的词,即将串频统计和串匹配结合起来,既发挥匹配分词切分速度快、效率高的特点,又利用了无词典分词结合上下文识别生词、自动消除歧义的优点。
到底哪种分词算法的准确度更高,目前并无定论。对于任何一个成熟的分词系统来说,不可能单独依靠某一种算法来实现,都需要综合不同的算法。笔者了解,海量科技的分词算法就采用“复方分词法”,所谓复方,相当于用中药中的复方概念,即用不同的药才综合起来去医治疾病,同样,对于中文词的识别,需要多种算法来处理不同的问题。
3、关键问题
  1)通用词表和切分规范
  汉语的语素和单字词,合成词和短语之间没有清晰的界限。语言学界虽然对于词在概念上有一个十分清晰的定义,即,“词是最小的能够独立活动的有意义的语言成分。”但从一些词典的编撰中,我们仍然可看出一些上述界限难以区分的问题。比如:“听见”“看见”在很多词典中都有收录,但是有类似结构的“闻见”却没有收录。在建立分词系统词表时,仍然对于收词的标准难以把握,例如:“鸡蛋”是词,那么“鸭蛋、鹌鹑蛋”是否也作为词收入词表?至今为止,分词系统仍然没有一个统一的具有权威性的分词词表作为分词依据。这不能不说是分词系统所面临的首要问题。除了分词词表,还有一个概念值得我们注意,即“分词单位”。从计算机进行分词的过程来看,其输出的词串我们称之为“切分单位”或“分词单位”。《信息处理用现代汉语分词规范》中对于“分词单位”也有一个定义:“汉语信息处理使用的、具有确定的语义或语法功能的基本单位。包括本规范的规则限定的词和词组。”由此可见,信息处理中分词单位的定义比传统意义上的词更宽泛些。这也就避开了理论上对于词的界定难以把握的困扰。分词系统可以面向解决实际问题的需求和真实语料中使用的频繁程度来规定“分词单位”。分词单位可以是同词表中词完全一致,也可以是包含未登录词识别以及一些词法分析的切分单位, 例如,一些人名、地名、机构名、外国人译名,应予以识别和切分。一些动词和形容词重叠结构,如“高高大大”、“甜甜蜜蜜”等;一些附加词,如后缀,“亲和性”、“热敏性”等;都可以作为分词单位予以识别和切分。因此,对于一个分词系统而言,制定一个一致性的分词单位切分规范无疑也是一个重要的问题。
2)歧义切分字段
  分词系统要处理的第二个关键问题是文本中歧义切分字段的判别。汉语中歧义切分字段最基本有以下两种类型:
  交集型歧义字段,据统计,这种歧义字段占全部歧义字段的85%以上。[4]所以这也是分词系统所要重点解决的问题。在字段ABC中,这里,A,B,C分别代表有一个或多个汉字组成的字串。A,AB,BC,C分别都是词表中的词,则称该字段为交集型歧义字段。如:“中国/人”,“中/国人”两种切分结果。
组合型歧义在字段ABC中, A,B,AB 分别都是词表中的词,则称该字段为交集型歧义字段。如:他/具有/非凡/的/才能/。/ 只有/他/才/能/举起/这/个/重物/。/
3)未登录词识别
  我们知道,词表中不能囊括所有的词。一方面是因为语言在不断的发展和变化,新词会不断的出现。另一方面是因为词的衍生现象非常普遍,没有必要把所有的衍生词都收入辞典中。
  特别是人名、地名等专有名词,在文本中有非常高的使用频度和比例。而且由于未录词引入的分词错误往往比单纯的词表切分歧义还要严重。这就要求分词系统具有一定的未登录词识别能力,从而提高分词的正确性。 除了人名、地名的识别,我们认为,分词系统还需要有一定的词法分析能力,从而解决衍生词和复合词等词汇平面上的问题,为进一步的中文信息处理提供坚实的基础。
切分原理
1、 词库组织
本人采用的是基于词表匹配的分词方法,因而词库是分词系统的基础。整个分词过程实际上就是在词词上的查找匹配过程,因而词库的组织相当重要。
对于词表存放在一个文本文件里,每一个词条由两项组成,一个是词的ID(WordId)、另一个就是词本身。同时在词表上加上静态索引,本人对词表进行分组管理并在上面添加三级索引。首先对词条按字数分组,字数相同的词条放在同一组里,并对词表按首汉字的内码从小到大排序。一级索引是加在各个分组上,一级索引记录了各分组的开始位置,再根据下一分组的起始位置可以确定当前分组的终止位置。二级索引是加在一级索引内部的,在同一组内部由于有很多的词条,二级索引是按词的首汉字内码建立的,它加在以不同汉字开头的词条组中,这样通过三级索引可以进一步缩小查找范围。另外在汉字中以有些字开头的词条过多,这样进行匹配的次数过多,不利于提高匹配速度。因而在二级索引的基础之上添加一个三级索引,它是按照一定的密度间隔添加,我设定了一个默认的合理的值就是每隔50个词条添加一个三级索引,同样三级索引也是根据汉字内码添加的(三级索引和二级索引的定义相同)。词条及索引的定义如下:
//根据汉字内码建立的索引结点(二级和三级索引)

69,369

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧