读SIM源码笔记 -- 1

背景

人生第一次读项目源码。

这是SIM查重工具,由大牛Dick Grune创造。文档和源码都可以在他的网站里找到。Paper是.ps文件,需要下载Adobe Acrobat打开阅读,也可以用Acrobat把它导出成pdf,用习惯的pdf阅读器看。

关于怎么运行他的源码,我的上一篇博客记录了配置MinGW的过程。当然,只是使用的话,直接下exe包就行了。

前段时间云里雾里地读了一下SIM提供的三个文档,Manual,Paper,Technique Report,然后这几天一头扎进源码里面,希望细节性地了解了它的实现以后,试图做一点创新,以此作为我大创项目的主要工作(这都三月份了,大创四月份就要结题了,说实话慌得很)。

第一次读源码,没有经验,就一头扎到main里面一层层地读下去了,过程中有一些心得随缘地记录一下。

 

前言

光对着源码看几秒钟就走神,所以一边写一边看来让自己注意力集中,这就是一篇笔记,质量就不强求了。

 

因为是一头扎到main()里面去看的,所以就从main讲起吧。

初看sim.c

main()在sim.c里面,所以我就从sim.c开始看起了。有一个感受,就是开始写这篇博客的时候看过的函数已经遍布五六七八个文件了,但是每个文件的代码量其实都在差不多的范围里,差不多都是200到400行的样子,这里面是有不少封装的思想。

oplist[]

这个文件里main()之前的内容并不多,最显眼的还是那一长串的oplist,这个是选项列表,因为sim工具给用户提供了许多的设置选项,可以根据不同需要进行查重,比如哪些文件不需要相互进行比较(old文件)、要不要按百分比显示查重率、百分之多少以上的重复率输出等等。

这个指令列表预设了这些指令,它是stuct option*类型,关于这个结构体,需要注意的就是里面有一个enum类型的变量,它指示的是这个指令后面所跟的参数,比如-r后面需要跟一个整型参数,表示MinRunSize被调整成多少。

读入参数指令

在Read_Input_Files(argc, argv)被调用之前,其实都是一些准备的工作,比如把版本号设置出来,保留progname,读入从命令行得到的参数,调用do_options,把这些参数都存到options列表当中去,用于后面检查哪些参数是被设定的。可以说主要就是对输入的参数指令进行检查,再做一些处理,比如如果接收到-v这个参数,就可以直接输出版本号结束程序了。

这里面应该说,感受到真正的项目和我之前做的算法题,还有我做的一些大作业比起来,它的出错处理就做得很精细了,在这里的准备工作里面,就对各种命令之间的冲突做了判断,如果有所错误,会进行相应的报错,然后退出。

读入文件

主要是Read_Input_Files的调用。

其实就是这一块略略读完之后,激发了我写博客的动力。之前也就碰到个结构体,是option,到这里它开始了层层调用,并且出现了text这个结构体,我怕不写一下博客,过几天回来看就忘了这一块在干什么了。

/*这是text类型的定义*/ 
struct text {
	const char *tx_fname;	/* the file name */
	size_t tx_start;	/* index of first token in Token_Array[]
				   belonging to the text */
	size_t tx_limit;	/* index of first position in Token_Array[]
				   not belonging to the text */
	/* 就表示这一段在Token_Array里面是[tx_start, tx_limit) */
	int tx_EOL_terminated;	/* Boolean */ /*暂时不知道干嘛的*/
	struct position *tx_pos;/* list of positions in this file that are
				   part of a chunk; sorted and updated by
				   Pass 2
				*/
};

struct position {
	/* position of first and last token of a chunk */
	struct position *ps_next;
	int ps_type;		/* first = 0, last = 1, for debugging */
	size_t ps_tk_cnt;	/* in tokens; set by add_run()
				   in Read_Input_Files() */  /*最后用来输出匹配数量*/
	size_t ps_nl_cnt;	/* same, in line numbers;set by Retrieve_Runs(),
				   used by Print_Runs(), to report line numbers
				*/  /* 最后用来输出匹配行号 */
};

 其实感觉text已经像是被封装成一个类了,不过只是封装的感受强烈,OOP当中多态和继承的思想它没有用到(也不需要吧)。目前对text的理解,就是它其实是指示了,在Token_Array当中,当前这个文件从第几号Token开始,从第几号Token结果。position的属性要等到pass2再来详看作用了。

Read_Input_File的调用一层又一层,尤其是“打开文件”这一个功能,后面还有流式文件的封装,给我绕晕了。琢磨了一段时间发现没啥效果,我就“抓大放小”地把后面几层调用先给放了。总的来说,这个模块就是把打开的文件中的token识别出来,存放在Token_Array数组当中。追溯获得token的方法,就是通过flex的yylex()来获得。词法分析和token的存储还蛮重要的,第一遍看没有能特别看得细,后面可能需要回来细看。

Compare_Files()

main()当中对这个函数的注释是"turns texts into runs",我在看文档的时候就对run这个单位有些迷惑,这部分明显是真正开始核心工作的地方。

这个函数又由三个函数嵌套形成:

void
Compare_Files(void) {
	Make_Forward_References();
	compare_texts();
	Free_Forward_References();
}

Make_Forward_References()

从TechnReport里大概了解到Foward_References,它是加速匹配的一种方法,但是看的时候对使用last_index[]对Foward_References进行更新的过程有所迷惑。这里方便起见,翻译一下TechnReport,也是注释里,对这个过程的描述。

文件的比较方法是,每个子串都和它所有右边的子串进行匹配,这个过程实际上是平方的复杂度,但是由于我们只会去关注超过'Min_Run_Size'长度字串的匹配结果,这就是我们有可能使用哈希表进行优化。

对文本中的每个位置p,我们构造一个下标数组

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值