一种计算机软件源代码相似度检测方法.pdf

上传人:小** 文档编号:6347110 上传时间:2019-06-03 格式:PDF 页数:28 大小:2.37MB
返回 下载 相关 举报
摘要
申请专利号:

CN201510794525.1

申请日:

2015.11.18

公开号:

CN105426711A

公开日:

2016.03.23

当前法律状态:

授权

有效性:

有权

法律详情:

授权|||实质审查的生效IPC(主分类):G06F 21/16申请日:20151118|||公开

IPC分类号:

G06F21/16(2013.01)I

主分类号:

G06F21/16

申请人:

北京理工大学

发明人:

嵩天; 田星; 李凤霞; 刘政祎

地址:

100081北京市海淀区中关村南大街5号北京理工大学

优先权:

专利代理机构:

代理人:

PDF下载: PDF下载
内容摘要

本发明涉及一种计算机软件源代码相似度检测方法,属于计算机应用技术领域,该方法包括如下步骤:首先依据编程语言不同,对源代码进行分词操作,然后选择特定标注词对分词结果进行分块处理,依据变量属性对变量分词进行相关处理;而后基于分块结果对各块进行差异测算操作,得到差异矩阵,依据各块差异结果及相关性,得到整体差异;进而根据公式最终得到代码相似度检测结果。对比现有技术,本发明方法能够比较成功的识别代码相似度检测中遇到的逐字拷贝、更改注释语句空白区域、重命名标识符、更改数据类型等手段,能够成功检测改变代码块顺序、改变语句顺序、增加冗余语句和变量以及用等价的控制结构替换原有控制结构等手段。

权利要求书

1.一种计算机软件源代码相似度检测方法,其特征在于,包括
以下步骤:
步骤1、将两份代码文件,依据其编程语言编译原理进行分词操
作,得到分词结果集合tokens;
步骤2、对两份代码tokens分别对以特定token起始的语法代码
块结构进行分块操作,得到其各自的分块结果blocks;
步骤3、对blocks中的变量类型token进行标记,统计其在全部
代码中出现的次数和来自的block;
步骤4、采用差异度计算方法计算代码文件A中的块与代码文件
B中的块的差异度,最终得到一个差异度矩阵;
步骤5、依据两份代码其各块的差异度关系,以及相似代码块间
差异度最小原则制定相应规则,选择差异度矩阵中的某些元素作为部
分差异度,或用某些代码块全部token个数作为部分差异度,对部分
差异度进行求和,得到两份代码整体差异度;
步骤6、依据两份代码整体差异度和两份代码总体token个数进
行计算,得到差异代码所占总体的比例,从而得到两份代码相似度结
果。
2.根据权利要求1所述的一种计算机软件源代码相似度检测方
法,其特征在于:步骤1所述分词操作通过以下过程完成:
首先,对代码文件,依据其所属编程语言词法规则进行初步分词,
得到由若干分词结果(token)组成的集合tokens;
其次,对初步分词结果进行再处理,删除注释或物理空行,并将
预先设定的代码块起始关键字token类型改为新的BLOCKNAME类
型,其他关键字保持原关键字类型不变。
3.根据权利要求2所述的一种计算机软件源代码相似度检测方
法,其特征在于:所述预先设定的代码块起始关键字为循环代码块、
判断代码块、函数代码块、类代码块、异常代码块、with代码块以及
全局代码块的起始关键字。
4.根据权利要求1所述的一种计算机软件源代码相似度检测方
法,其特征在于:步骤2所述进行token分块操作时,对于某些起始
关键字开始的块,根据语言语法规则设定块相关性,对具有相关性的
连续块合并成一个代码块。
5.根据权利要求1或4所述的一种计算机软件源代码相似度检测
方法,其特征在于:所述进行token分块操作时对于仅包含一条语句
的代码块不作为一个单独的块。
6.根据权利要求1所述的一种计算机软件源代码相似度检测方
法,其特征在于:步骤2所述对blocks中变量类型token进行标记,
标记方法为将该token写为三元组形式[value,-1*次数,blockname],其
中value表示该token的字符串原文,次数表示该token出现的次数,
blockname表示该token来自于哪个块,如果是全局块以'global'表示,
如果是其它块以其对应块的起始关键字表示。
7.根据权利要求1所述的一种计算机软件源代码相似度检测方
法,其特征在于:步骤4所述差异度计算方法为如下一种改进的编辑
距离算法:
(1)查找代码块A、B的最长连续公共token子序列;
(2)如果该序列长度小于预设的最小语句长度MIN,则转到步
骤(3);否则,用一个不同于已有token的新的token替换代码块A、
B的最长连续公共token子序列,然后返回步骤(1);
(3)以传统的编辑距离算法计算代码块A、B的差异度。
8.根据权利要求1所述的一种计算机软件源代码相似度检测方
法,其特征在于:判断所述变量类型token相同的标准如下:
(1)如果两个token的名称相同,则视为相同;
(2)如果名称不同,但在两份代码中出现次数相同或相近且较
大,则视为相同;
(3)如果名称不同,但在两份代码中出现次数相同或相近且较
小,且所属块相同,则视为相同。
9.根据权利要求1所述的一种计算机软件源代码相似度检测方
法,其特征在于:步骤5所述依据差异度矩阵以及相似代码块间差异
度最小原则制定的相应规则如下:
(1)对差异度矩阵,对于每一行选择不与已选取过值的列相重
复的最小值作为该行的差异度;
(2)对于剩余没有被取过值的行或列,选取对应代码文件中代
码块的token个数作为该行或列的差异度。
10.根据权利要求1所述的一种计算机软件源代码相似度检测方
法,其特征在于:步骤6所述依据两份代码整体差异度和两份代码总
体token个数进行计算,过程如下:
(1)依据公式计算初步相似度结果评分Score:
S c o r e = 1 - e d i t D i s ( A , B ) ( ( c a r d ( A ) + c a r d ( B ) ) / 2 ]]>
其中card(·)表示·中的全部token个数,editDis(A,B)表示代码块
A、B的整体差异度;
(2)在输出所述Score前,通过以下过程进行修正:如果score
大于1,则将score修改为1;如果score小于0,则将score修改为0。

说明书

一种计算机软件源代码相似度检测方法

技术领域:

本发明涉及一种计算机程序分析技术和计算机软件的代码相似度检测算法,
特别涉及一种基于源代码分词分块提取处理及多种差异测量方法可扩展的代码
相似度检测算法,属于计算机应用技术领域。

背景技术

代码相似度检测技术目前最主要应用于代码的剽窃检测上,是计算机软件
开发和维护活动中一项重要任务,在源代码剽窃检测、软件组件库查询、软件
缺陷检测、程序理解等多个领域中有广泛应用。其不仅可以帮助教师检测学生
的程序作业抄袭情况,同时对软件版权的鉴定也具有好的现实意义。

在发表于第六届北美大学计算科学协会年会论文集“抄袭检测度量元”
(Metricsbasedplagiarismmonitoring.Paperpresentedatthe6thAnnualCCSC
NortheasternConference,MiddleburyVT.2001)一文中,琼斯(Jones)总结了十种剽
窃手段。分别为(1)逐字拷贝(2)更改注释语句(3)更改空白区域(4)重
新命名标识符(5)改变代码块的顺序(6)改变代码块中语句的顺序(7)改变
表达式中操作符和操作数的顺序(8)更改数据类型(9)增加冗余的语句和变
量(10)用等价的控制结构替换原有控制结构。

从国内外的研究现状可以发现,国内在对程序相似度判别研究相对较少,
大部分集中在对中文分词和语义的研究上。可以参考的工具有:北航高级程序
课程教学辅助平台中用于检测学生提交程序作业是否抄袭的BUAASIM系统等。

国外目前已有许多软件工具可用来检测源程序是否抄袭,如美国斯坦福大
学的MOSS系统、威奇塔州立大学的SIM系统、伊利诺伊大学的GPLAG系统、
德国Karlsruhe大学的JPlag系统和澳大利亚悉尼大学的YAP3系统。目前最主
要的代码相似度检测技术主要分为两大类,属性计数技术及结构度量技术,包
含如下几种具体的方法:Textualcomparison,Tokencomparison,Metriccomparison,
Comparisonofabstractsyntaxtrees(AST,抽象语法树),Comparisonofprogram
dependencygraphs(PDG,程序依赖图)及其他相关方法。

而在实际使用及研究中发现,目前主要流行的上述几种方法各自存在其不
同的缺陷。两种token-based的方法和text-based的方法效果一般且相差不大。
AST-based的方法效果表现很好,但算法流程复杂,难于实现且执行时间长,且
对于不同语言的匹配实现算法改动较大,PDG-based的方法表现性能不佳。另有
文章指出,上述五种方法均拒绝了大量真正的相似代码,且其对于注入代码等
某些特殊情况检测率低。

综上,目前主流的代码相似度检测方法普遍存在检测效果低、部分方法复
杂执行时间长、某些情况错误率高以及难于应用在不同编程语言上等问题。

发明内容

本发明的目的是为解决在检测代码相似度过程中遇到的检测效果低、部分
方法复杂执行时间长、某些情况错误率高以及难于应用在不同编程语言上等问
题,提出了一种计算机软件源代码相似度检测方法,该方法基于源代码分词处
理和分块分析比较差异获得代码相似度检测结果。应用该方法,可以在低内存
短执行时间的前提下,显著提升代码相似度检测结果准确度,并大幅度降低代
码检测最短代码长度限制。

本发明方法的思想是,首先依据编程语言不同,对源代码进行分词操作,
然后选择特定标注词对分词结果进行分块处理,依据变量属性对变量分词进行
相关处理;而后基于分块结果对各块进行差异测算操作,得到差异矩阵,依据
各块差异结果及相关性,得到整体差异;从而最终得到代码相似度检测结果。

一种基于源代码分词分块提取处理及多种差异测量方法可扩展的代码相似
度检测方法,包括以下步骤:

步骤1,将两份代码文件,依据其编程语言编译原理进行分词操作,得到分
词结果集合tokens;

步骤2,对两份代码tokens分别对以特定token起始的语法代码块结构,得
到其各自分块结果blocks;

步骤3,对blocks中的变量类型token进行标记,统计其在全部代码中出现
的次数和来自的block;

步骤4,采用差异度计算方法计算代码文件A中块与代码文件B中块的差
异度,最终得到一个差异度矩阵

步骤5,依据两份代码其各块的差异度关系,以及相似代码块间差异度
最小原则制定相应规则,选择差异度矩阵中的某些元素或用某些代码块全
部token个数作为部分差异度,对其进行求和,得到两份代码整体差异度;

步骤6,依据两份代码整体差异度和两份代码总体token个数进行计算,
得到差异代码占代码总体的比例,进而得到两份代码相似度结果。

最终相似度结果为一个在[0,1]区间的实数,0表示非常不相似,1表示非常
相似。

作为优选,步骤1所述分词操作通过以下过程完成:

首先,对代码文件,依据其所属编程语言词法规则进行初步分词,得到由
若干分词结果(token)组成的集合tokens;

其次,对初步分词结果进行再处理,删除注释或物理空行,并将预先设定
的代码块起始关键字token类型改为新的BLOCKNAME类型,其他关键字保持
原关键字类型不变。

作为优选,所述预先设定的代码块起始关键字为循环代码块、判断代码块、
函数代码块、类代码块、异常(try)代码块、with代码块以及全局代码块的起
始关键字。

作为优选,步骤2所述进行token分块操作时对于某些起始关键字开始的
块,根据语言语法规则设定块相关性,对具有相关性的连续块合并成一个
代码块。

作为优选,所述进行token分块操作时对于仅包含一条语句的代码块不作
为一个单独的块。

作为优选,步骤2所述对blocks中变量类型token进行标记,标记方法为将
该token写为三元组形式[value,-1*次数,blockname],其中value表示该token
的字符串原文,次数表示该token出现的次数,blockname表示该token来自于
哪个块,如果是全局块以'global'表示,如果是其它块以其对应块的起始关键
字表示。

作为优选,步骤4所述差异度计算方法为如下一种改进的编辑距离算法:

(1)查找代码块A、B的最长连续公共token子序列;

(2)如果该序列长度小于预设的最小语句长度MIN,则转到步骤(3);否
则,用一个不同于已有token的新的token替换代码块A、B的最长连续公共token
子序列,然后返回步骤(1);

(3)以传统的编辑距离算法计算代码块A、B的差异度。

作为优选,判断所述变量类型token相同的标准如下:

(1)如果两个token的名称相同,则视为相同;

(2)如果名称不同,但在两份代码中出现次数相同或相近且较大,则视为
相同;

(3)如果名称不同,但在两份代码中出现次数相同或相近且较小,且所属
块相同,则视为相同。

作为优选,步骤5所述依据差异度矩阵以及相似代码块间差异度最小原则
制定的相应规则如下:

(1)对差异度矩阵,对于每一行选择不与已选取过值的列相重复的最小值
作为该行的差异度;

(2)对于剩余没有被取过值的行或列,选取对应代码文件中代码块的token
个数作为该行或列的差异度

作为优选,步骤6所述依据两份代码整体差异度和两份代码总体token个数
进行计算,过程如下:

(1)依据如下公式计算初步相似度结果评分Score:

S c o r e = 1 - e d i t D i s ( A , B ) ( ( c a r d ( A ) + c a r d ( B ) ) / 2 ]]>

其中card(·)表示·中的全部token个数,editDis()表示代码块A、B的整体差
异度;

(2)在输出所述Score前,通过以下过程进行修正:如果score大于1,则
将score修改为1;如果score小于0,则将score修改为0。

有益效果

对比现有技术,本发明的有益之处在于,本技术能够比较成功的解决代码
相似度检测中遇到的逐字拷贝、更改注释语句空白区域、重命名标识符、更改
数据类型等手段,并通过使用类似于GST(GreedyStringTiling)字符串匹配
算法改写编辑距离算法或其他度量方法,并采用本方法设计实现的分块比较编
辑距离方法,使其能够成功检测改变代码块顺序、改变语句顺序、增加冗余语
句和变量以及用等价的控制结构替换原有控制结构等手段。

最终得到的结果能够准确体现出相似部分代码占代码整体的百分比。本方
法不仅能使用现有的多种编辑距离算法,使用者也可以在本方法思想基础上使
用其他差异测算算法,以适应不同场景。

附图说明

图1为算法流程图。

图2为示例代码1。

图3为示例代码2。

图4为代码1转化得到的tokens。

图5为代码2转化得到的tokens。

图6为代码1转化得到修改变量后的分块结果。

图7为代码2转化得到修改变量后的分块结果。

图8为两份示例代码编辑距离矩阵。

具体实施方式

为使发明的目的、技术方案及优点更加清楚,下面将结合附图对本发明的
实施例进行详细描述。本实施例以本发明技术方案为前提进行实施,给出了详
细实施方式和具体操作过程,但本发明的保护范围不限于下述实施例。

本方法并不仅针对某一种编程语言,此处为便于详细介绍具体实施方式,
故选择两份Python语言的程序代码作为示例。

考虑如图2、图3所示两份代码,代码一为原创代码,代码二为相似代码,
主要包括以下几种改动方式:(1)逐字拷贝(2)更改注释语句空白区域(3)
重命名标识符(4)改变语句顺序(5)改变代码块顺序(6)增加冗余语句和变
量(7)等价的控制结构替换原有控制结构;如表1所示。

表1抄袭手段及百分比


下面根据如图1所示算法流程,对如图2、图3所示的2份示例代码进行相
似度检测:

步骤1至步骤3均为对两份代码独立进行处理,步骤4-6为将两份代码相关
计算结果合在一起进行处理。

步骤1:将两份代码文件,依据其编程语言相关的编译原理进行分词操作,
得到分词结果集合tokens;

本步骤是对两份待比较代码进行读取分词处理。本步骤的目的是将原有代
码转换为tokens,以供后文进行差异度计算;并将预先设定的代码块起始token
与普通关键字token进行分离,以便后文进行分块处理。

读取给定的两份待比较代码源文件,本例中采用如图2、图3所示的两份
Python代码源文件进行代码相似度比较。

首先,对代码文件,依据其所属编程语言词法规则进行初步分词,得到由
若干分词结果(token)组成的集合tokens。

该分词结果是由分词内容(value)及分词类型(type)构成的二元组(token
[value,type]);进一步的,为了便于后续处理中进行比较,可用其类型对应的唯
一标号替代。Python代码文件初步分词操作可直接使用ThePythonStandard
Library中的tokenize模块完成。

其次,对初步分词结果进行再处理,删除注释或物理空行,并将预先设定
的代码块起始关键字token类型改为新的BLOCKNAME类型,其他关键字保持
原关键字类型不变。

本例中,代码文件初始分词类型参照Python源码Lib/token.py文件。

为下一步进行分块处理而预先设定的代码块为:判断代码块、循环代码块、
函数代码块、类代码块、try代码块、with代码块以及全局代码块。故此处选择
Python编程语言中的if、for、def、class、while、try和with共7个关键字作为
BLOCKNAME类型。全局代码块的起始标志为初步分词结果tokens的第一个
token。因此,针对本步骤内容,此处对tokens中的每一个元素token进行如下
判断处理:

1.如果其type表示类型为注释或物理空行等,即其在代码实际执行中无意
义,则将其从tokens数组中删除;

2.如果其type表示关键字类型,并且其是预先设定的BLOCKNAME中的
任意一个,则将该token内容替换为[value,BLOCKNAME];

3.如果是其他情况,则保持原token不变。

以上设定的代码块仅为举例,实际应用过程中可以根据不同语言的特点进
行设定。

由于Python语言tokenize模块进行分词后,会将用户自定义变量名、函数
名等与全部Python保留关键字作为同一个类型NAME。为便于后文对变量名进
行去重复操作,此处需要将用户自定义变量名与Python保留关键字区分开。故
设立新的分词类型WORD,用来表示除BLOCKNAME所表示的7个关键字之
外的其他保留关键字。用户自定义变量名、函数名均保持原type不变,即仍为
NAME类型。

因此,对于Python语言的代码,需在上述判断中额外添加一条判断分支,
即:

4.如果其type为NAME,且其是除BLOCKNAME之外的Python保留关键
字,则将原token内容替换为[value,WORD]。

至此,对两份待比较代码分词处理完毕。本例中得到的分词结果如图4、图
5所示。

本步骤中,使用者可以依据不同编程语言的编译原理,对待检测相似度的
程序代码进行分词操作,并不仅限于使用tokenize模块。

分词类型依据编程语言不同而不同,普遍存在的类型如关键字、操作符、
界限符等,每个类型均有独一的标号,此标号仅作为标识,不影响后文操作,
使用者可自行确定标号。BLOCKNAME依据不同编程语言的语法而确定,并不
仅限于上文所述。

另外需要注意的是,为方便后文对变量进行代码块的标记,需要其所属代
码块名;全局代码块的起始标志为tokens数组第一个token的value。ThePython
StandardLibrary中的tokenize模块对Python源码进行初步分词操作时,会默认
在结果tokens中第一个位置添加一个ENCODING类型token,表示该代码文件
所使用的编码方式;其也会在结果tokens中最后一个位置添加一个
ENDMARKER类型token,表示该代码文件的结束。如果使用者使用其他分词
工具未有类似操作时,可以在步骤1结束后手动添加一个代码文件起始token和
一个代码文件结束token。

步骤2:对两份代码的tokens分别以特定token起始的语法代码块结构进行
分块操作,得到各自的分块结果blocks;。

本步骤将分词结果tokens作为输入,依据预先设定的BLOCKNAME进行
分块处理,得到分块结果blocks。

一般编程语言代码块token可能的结构如下所示:

(1)…[value1,BLOCKNAME],[value2,代码块起始标志类型],…[value3,代
码块结束标志类型],…

(2)…[value1,BLOCKNAME],[value2,代码块起始标志类型],…[value3,代
码块结束标志类型],[elseif、else、except、finally或其他表示与value1同一判断
条件不同分支的关键字类型],[value4,代码块起始标志类型],…[value5,代码块结
束标志类型],…

(3)…[value1,BLOCKNAME],[value2,代码块起始标志类型],…
[value3,BLOCKNAME],[value4,代码块起始标志类型],…[value5,代码块结束标志
类型]…[value6,代码块结束标志类型],…

结构(1)为普通顺序代码块结构;结构(2)为在代码块结尾处有该代码
块所属判断条件或其他关系的另一分支代码块结构;结构(3)为在以value1起
始BLOCKNAME的代码块中,嵌套有另一以value3为起始BLOCKNAME的代
码块。

由于代码块之间可能有并列或嵌套关系,为了明确处理代码之间的嵌套关
系,引入代码块替代常量FOLDEDBLOCK,表示一段被提取出去的代码块。

针对上述三种代码块结构的分块操作分别为:

(1)将从[value1,BLOCKNAME]起始至[value3,代码块结束标志类型]结束
的若干token划分为一个代码块;

(2)将从[value1,BLOCKNAME]起始至[value5,代码块结束标志类型]结束
的若干token划分为一个代码块;

(3)将从[value3,BLOCKNAME]起始至[value5,代码块结束标志类型]结束
的若干token划分为一个代码块,将从[value1,BLOCKNAME]起始至[value6,代码
块结束标志类型]的若干token划分为一个代码块。

然后用一个token[代码块名,FOLDEDBLOCK],在原tokens数组中替换划
分出去的代码块。

针对上述如何划分代码块的内容,本实施例给出如下具体划分过程的详细
流程:

首先,设立4个变量:flag,布尔型变量,其初始值为True,若当前已读取
到文件结束标志则置为False;pos,整型变量,表示当前代码块在tokens数组中
的起始位置;blocks,数组类型变量,用于存储提取出来的代码块结果;stack,
数组类型变量,作为栈存储检测到的代码块名及其起始位置index,代码块名
(block)初始值为代码块起始关键字,且首字母大写,例如,with关键字的块
名为With。处理过程中,通过字母大小写变化表示代码块处理状态,大写字母
表示该代码块未处理,小写字母表示当前正在处理,出栈表示处理完成。

然后,设立两层循环,外层为while循环,条件为flag为True时执行循环,
为False时退出循环;内层为for循环,数组下标临时变量i从pos起始,依次
增长到tokens数组最后一位,每次增长1;外层循环直接嵌套内层循环。

在内层循环中,执行如下判断:

1.如果当前token.type为BLOCKNAME,则执行操作为,获得其代码块名,
将元素[代码块名首字母的大写字母,数组当前下标i]入栈;

2.如果当前token.type为代码块起始符号,并且stack中至少存在一个未处
理过的block,即stack栈顶block值改为大写,然后执行以下判断:

(1)如果tokens数组当前位置下一位无token,即代码文件结束,表示该
代码文件存在语法错误,则执行操作为:返回错值,程序中止;

(2)其他情况,执行操作为,将stack中栈顶block值修改为小写,表示当
前正在向该block中添加token,继续执行下一次循环。

上述2个判断条件为并列判断,即if、else关系。

3.如果当前token.type为代码块结束标志,则执行操作为,进行如下判断:

(1)如果当前栈为空,表示该代码文件存在语法错误,则执行操作为,返
回错值,程序中止;

(2)如果tokens数组当前位置下一位有token,并且其value值是else、except、
finally或elif中的一个或表示与前一代码块BLOCKNAME同一判断条件不同分
支的关键字,则执行操作为,执行内层for循环的下一轮,即continue操作;

(3)如果并非上述两种情况,则执行操作为,将stack中栈顶元素,即当
前正在处理中的代码块,的index值至当前位置i的全部tokens作为一个block
加入blocks数组中,然后将tokens数组中的这部分token用一个[代码块名,
FOLDEDBLOCK]替代,pos位置更新为stack栈顶元素index+1,而后将栈顶元
素从stack中移出,即出栈操作,然后退出内层for循环,进入最外层while循
环继续执行,即break操作。

上述3个判断条件为并列判断,即if、elseif、else关系。

4.如果当前遍历的token类型为ENDMARKER(见步骤1),则表明当前代
码文件读取处理完毕,则执行操作为,将当前tokens数组中的所有token作为一
个block加入到blocks中,退出全部循环;

5.其他情况,不进行任何操作,进行下一轮循环。

上述5个判断条件为并列判断,即if、elseif、elseif、elseif、else关系。

至此,对tokens分块处理完毕。本步骤中,使用者也可依据不同编程语言、
或不同的认知选择不同于本例的起始关键字。

由于Python编程语言中,NEWLINE表示逻辑换行,INDENT表示缩进,
两者连接在一起表示,在代码块起始BLOCKNAME后面是一个合法的代码块;
DEDENT,表示回退,表示该代码块结束。其正常代码块token结构如下:

…[value1,BLOCKNAME],[value2,NEWLINE],[value3,INDENT],…
[value4,DEDENT],…

或…[value1,BLOCKNAME],…

第二种情况主要适用于该代码块仅包含一条语句的情况,故其不需要代码
块起始标志类型token与代码块结束标志类型token。本方法中选择将该情况不
作为一个独立的代码块进行处理。

依据上述内容对原方法进行改动,具体改动主要在内层循环判断中的第2
点中,改动后的第2点如下:

2.如果当前遍历的token的类型为NEWLINE,并且stack中至少存在一个
未处理过的block,即stack栈顶元素block值的符号为大写,则执行操作为,进
行以下判断:

(1)如果tokens数组当前位置下一位无token,即代码文件结束,表示该
代码文件存在语法错误,则执行操作为,返回错值,程序中止;

(2)如果数组下一位token类型为INDENT,执行操作为,将stack中栈顶
block值修改为小写,表示当前正在向该block中添加token;

(3)其他情况,执行操作为,将stack栈顶元素出栈,即表示将一行的块
作为普通语句,不作为块进行处理;

上述3个判断条件为并列判断,即if、elseif、else关系。

使用者可依据本方法思想,结合具体编程语言token语法,选取不同于本例
的起始关键字或对本步骤中内容进行改动。

步骤3:对blocks中的变量类型token进行标记,统计其在全部代码中出现
的次数和来自的block;

本步骤是对得到的tokens分组中变量类型的token进行标记。目的是依据该
变量的命名、出现次数及其出现位置,唯一确定该变量,以应对变量名替换等
问题。因此,结合Python语言的特点,本实施例中将变量类型token改写为三
元组形式[value,-1*次数,blockname],其中次数表示该变量在所有token中出现
的次数,blockname表示该变量来自于哪个块,如果是全局块以'global'表示,如
果是其它块以其对应块的起始关键字表示。此处由于token.type的标号为正值,
将三元组第二位设置为负值的意义在于,可使变量类型token出现的次数与
token.type的标号相区分。当然,对于变量类型的token的标记,也可根据实际
情况采取其它的方式进行标记,只要能够方便识别不同代码中的同一变量即可。

首先,设立一个名为name的字典类型变量,用于存储每一个变量类型token
的value及其在步骤1得到的全部tokens中出现的次数(value,count)。执行如
下操作:遍历步骤1得到的tokens数组,对于tokens数组中每一个token,如果
该token的type为变量类型,则执行如下判断:若该value尚未在name中出现
过,则将其value加入字典,并设置其值count=1;若出现过,则依据其value
使其对应计数count加1。可通过name[value]获得该value所对应的count值。

另设立一个名为from的字典类型变量,用于存储每一个变量类型token的
value及其在步骤2得到的分块blocks结果中,其声明位置所属代码块名(value,
fromblock)。可通过from[value]获得该value所对应的fromblock值。

其次,对于步骤2得到的分块blocks,使用双重循环进行遍历,对于每一个
block中的每一个token,如果其type为变量类型,则进行如下判断:

1.如果该token所属当前block中,起始token的type表示为该代码文件起
始token,则表明该block为全局block,则执行操作,将该位置token改写为[value,
-1*name[value],'global'],并将from[value]值置为'global';

2.如果该token所属当前block中,起始token的type为BLOCKNAME,
则表示该块为内部代码块,则执行操作,若from[value]值为空,则将该位置token
改写为[value,-1*name[value],block[0].value](block[0].value为当前block第一
个token的value),并将from[value]值置为block[0].value;若from[value]值非空,
则将该位置token改写为[value,-1*name[value],from[value]]。

此处注意,由于Python语言语法特殊性,可以将二元组与三元组同时放置
在同一个数组中;如果使用者使用其他语言实现本算法则需自行对该处进行改
写。

另外由于Python语言语法特殊性,变量全部为引用,不需要提前进行声明,
则本步骤中不需要设立from字典,且在判断中可直接参照from[value]值为空情
况进行处理。

至此,对分组后的变量类型token改写完毕。本例得到的分组结果如图6、
图7所示。

步骤4:采用差异度计算方法计算代码文件A中的块与代码文件B中的块
的差异度,最终得到一个差异度矩阵;

本步骤是对于两份待比较代码A、B,依据步骤3得到的改写变量后的分组
结果,计算代码A的每一个代码块对应于代码B的每一个代码块的差异度,以
此构成差异度矩阵,维度为M*N,其中,M、N分别表示A、B的代码块数。

在进行变量类型token比较时,设定2个token相同条件如下:

(1)如果token的count值均为负值,且名称,即value相同,则视为相同;

(2)如果名称不同,但出现次数,即count相同(或相近)且绝对值较大,
则视为相同;

(3)如果名称不同,但出现次数,即count值相同(或相近)且绝对值较
小,且所属块相同,则视为相同,

(4)其他情况视为不同。

使用者也可依据自身在前文中对变量改写时采用不同的改写方法,而在这
里使用不同的比较方法。

现有的差异度计算方法很多,都可以用来计算不同代码块之间的差异度。
为了获得更好的差异度计算结果,本例中采用一种发明人改进的编辑距离算法。
下面对该方法进行解释介绍。

本发明人对编辑距离算法的改进,是通过在待计算差异度的两个代码块中,
将所有长度在自设最小长度阈值(MIN)之上的相同连续token序列,替换为一
个token[folded,FOLDEDTOKEN],其中folded为标号,从0开始,每替换一
次进行加1操作,FOLDEDTOKEN为自设token的type。

该改进方式具体执行流程如下:

在说明流程前,先对其中涉及的符号含义进行说明:设立折叠相同代码块
标号folded,初始赋值为0。设定MIN值,本例中设为6,表示两个代码块A、
B中最长相同token序列长度。

第1步,查找代码块A、B的最长连续公共token子序列,此方法有多种实
现,故此处并不给出具体实现,记录该公共子序列长度为len、代码块A起始位
置startA和代码块B起始位置startB;

第2步,判断该子序列长度,若该序列长度小于MIN,则编辑距离算法改
进过程结束,不再继续执行;否则执行代码块折叠替换操作,即,用[(folded,
FOLDEDTOKEN)]替换代码块a、b中的相同位置tokens,即a[startA:startA+len-1]
与b[startB:startB+len-1];

第3步,对folded进行加1操作,然后,跳回到第1步,对更改后的代码
块A、B重新进行判断。

至此,对编辑距离算法的改进结束。

下面简要介绍传统的编辑距离算法,编辑距离(EditDistance),又称
Levenshtein距离,是指两个字串之间,由一个转成另一个所需的最少编辑操作
次数。许可的编辑操作包括将一个字符替换成另一字符,插入一个字符,删除
一个字符。一般来说,编辑距离越小,两个串的相似度越大。

简单来讲,编辑距离算法的输入为两个序列,通过使用者自定义的比较函
数进行相关比较,最终可以得到一个整数值,用于表示两个输入序列之间的编
辑距离,即差异度。该差异度越小,表示两个序列更相近。

具体计算编辑距离矩阵步骤如下:

第1步,计算两份代码A、B中各自包含的代码块个数为lenA、lenB,建
立一个二维数组,editM[lenA][lenB];

第2步,建立双层循环,外层循环遍历blocksA,内层循环遍历blocksB,
循环内通过改进后编辑距离算法计算blocksA[i]与blocksB[j]的编辑距离,将其
值存储在editM[i][j]中;

第3步,对editM二维数组进行调整,使其列数大于行数。

至此,各代码块间差异度矩阵计算完毕。此步骤得到差异度矩阵如图8所
示。

此改进方法主要是针对编辑距离算法中,对代码块替换操作差异度计算高
于准确值的问题进行的改进。在通常的编辑距离中,允许的编辑操作包括:插
入,删除,替换。但其不能解决如下问题,abcdefgh123456789与
123456789abcdefgh的比较。注意到这两个串仅是一个普通的调换,但用传统算
法计算出来的距离为16。这是因为编辑操作里没有块(块定义为大于某个阈值
长度的子串)的调换,如果采用原值16,则会导致差异度结果远大于准确值。

但如果结合最大公共子序列算法,使用本文的改进方法,则将原串替换为
AB和BA,即123456789替换为A,abcdefgh替换为B。此时再使用编辑距离
算法,得到距离为2。即可以理解为一次块的调换需要2次操作。

通过将原值由16降低到2,大幅降低了代码块或代码语句调换而带来的过
大的差异度问题。此改进方法克服了代码语句位置替换带来的干扰。

需要注意的是,在多次求最大公共子序列时,如果子序列长度小于某个阈
值,即停止该步骤,进入求编辑距离阶段;在阈值设置上可选择目标编程语言
的常见代码最小语句长度,如对于Python语言代码可将该值设为6,通常来说
最小语句为“A=a+b\n”,共6个tokens。

另外,此步骤中,使用者可以依据自身使用条件,选择使用不同差异度计
算算法,包括且不限于编辑距离算法、向量空间模型和基于哈希方法的相似计
算等;使用者也可以对其进行任何方式的改进修正。

步骤5:依据两份代码其各块的差异度关系,以及相似代码块间差异度最小
原则制定相应规则,选择差异度矩阵中的某些元素作为部分差异度,或用某些
代码块全部token个数作为部分差异度,对部分差异度进行求和,得到两份代码
整体差异度;

本步骤是选择合适的若干差异度值,并进行求和,得到两份代码总体差异
度。设二维矩阵中,行所代表的代码文件为代码A,列所代表的代码文件为代
码B。本步骤具体执行流程如下:

首先,设常量MAX_REPLACE,其值为代码A在步骤1得到的tokens的长
度;设数组flag,其长度为代码B中代码块的个数,各元素初值为0;设整型变
量score,初始赋值为0,代表最终相似度得分。

其次,按行遍历矩阵,求取矩阵每行最小值,则score累加当前最小值,并
将矩阵该最小值所在的列中的所有元素的值修改为MAX_REPLACE,以及修改
flag[i]为MAX_REPLACE。

此处遍历结束后,各行选择结果如图8中圆圈圈出的差异度值。

最后,遍历flag数组,如果该元素值为0,则score累加上该位置对应的列
所代表的代码块中包含的token的个数,即card(blocks[i]),其中card为计算块
中所包含的token个数函数。

flag数组遍历完成后,则该score值为差异度之和,即两份代码整体差异度。
至此两份代码整体差异度计算完毕。由图8,通过差异度矩阵各行最小值累加得
到的结果为185,只有第12列未被取值,而代码B中第12块包含的token个数
为4,因此本例中总体差异度为score=185+4=189。

本步骤的思想如下:差异度的意义是两份代码不同的token的总个数,也可
以理解为从一份代码改动到另一份代码需要改动的token总个数。为获得实际有
意义的改动次数,本方法对代码进行分块,依据各块的差异度,选取相近的两
个代码块间的差异度,最终求和得到总体差异度。

首先,选择矩阵每行中行列不交叉的最小值,意义是选择代码A中每块,
对应代码B中,其最相近的代码块的差异度,而行列不交叉的意义在于,代码
A中的代码块与B中的代码块是一一对应的,每个代码块不会被重复的选择两
次或以上。

其次,对于代码B中剩余的代码块的差异度的选取方法,其意义在于,如
果B中剩余块所在列中的差异度,没有在前一步中被选取,说明该块和A中任
意一块均不相似,其差异度即为其自身全部token总个数。

这样,无论代码A、B中任意一块代码块的差异度都在score值中有所体现,
保证了此处测算的总体差异度接近于真实值。

本步骤中,不同编程语言的处理均相同或相似。

步骤6:依据两份代码整体差异度和两份代码总体token个数进行计算,
得到差异代码所占总体的比例,从而得到两份代码相似度结果。

根据两份代码整体差异度及其tokens通过下式计算二者相似度score,并
将该值与预设的相似度阈值s进行比较,当score≥s时,输出检测结果为相似,
否则为不相似:

1 - e d i t D i s ( A , B ) ( ( c a r d ( A ) + c a r d ( B ) ) / 2 ]]>

其中,card(A)或card(B)分别表示A或B代码的全部token个数,editDis(A,B)
为两份代码整体差异度。

进一步的,由于差异度算法选择的不同,会带来整体差异度的过大(两份
代码几乎不相似)或过小(两份代码太相似),从而带来相似度计算结果会超
出[0,1]范围的情况,因此需要对score进行如下判断及修正:

1.如果score结果大于等于1,则将该结果修改为1.0(由于算法自身原因可
能会有该情况出现,并不影响实际结果);

2.如果score结果小于等于0.0,则将结果修改为0.0(同1.情况)。最终用
户可以依据实际使用情况,选择使用相似度精度。

最终score即为相似度结果,其为一个在[0,1]区间的实数表示,0表示非常
不相似,1表示非常相似。使用者可以依据自身设置相似度阈值s,给出相似检
测结果。本例中总体差异度为189,代码1tokens总数为570,代码2tokens总
数为595,因此score结果为0.676,设置的判断相似度阈值为0.2,则该两份代
码检测结果为相似。

本步骤的思想是:两份代码整体差异度即两份代码中不同的token总个数,
以其除以两份代码的平均token总个数,可以得到两份代码差异部分所占百分比,
由1减去该百分比,可以得到相似部分所占半分比。其值越接近1表明相似部
分所占比例越大,即两份代码越相似。

结论

在计算程序间相似度时,首先需要提取程序段的特征值,即能够代表该程序
内容和结构的基本语言单位。然后对提取出的待比较程序的特征值进行比较,
根据比较结果判断程序间的相似程度,即计算相似度。在这个过程中,特征值的
提取至关重要,特征值的好坏直接影响比较结果的准确性。

本方法提取的特征值即为分词结果,在背景中提出的token-base方法基础上,
增加了相同变量去重复、程序分块处理和改进差异度计算方法等操作,避免了
背景中提及的token-base方法普遍遇到的困难,提高了检测效果。本方法在学习
了PDG方法中的分块思想,舍弃了其依赖关系等方面内容,提高了程序的检测
效果,而由于本方法本是基于token-comparison方法,仅在一定程度上提高了算
法的复杂度,是可接受的。

本方法对于提到的几种特殊情况错误率高进行了修正优化,解决了注入代
码检测率低,真正相似代码被拒绝等问题。

本方法前后主要分为分词、分块、计算差异度、计算相似度结果四大部份
内容。分词是编译器在进行代码编译时的基本工作之一,此块内容可依据不同
语言的编译器来实现,不同语言处理得到的分词结果均可实现统一模式,且分
词操作可以直接由公开的几种方法实现,如Python的tokenize模块,Lex等。
分块是依据不同编程语言,代码块起始结束标志来定,不同语言起始关键字类
似,如if、for、while等,不同语言代码块作用范围结束标志由该语言自身决定,
如C++语言的{、}和Python语言的对齐等。差异度的计算也可替换为其他不同
计算方法。最终相似度的计算上主要是基于前面差异度的计算结果,不同编程
语言均相同或相似。故,本方法也解决了其他代码相似度检测结果难于应用的
问题。

以上所述的具体描述,对发明的目的、技术方案和有益效果进行了进一步
详细说明,所应理解的是,以上所述仅为本发明的具体实施例而已,并不用于
限定本发明的保护范围,凡在本发明的精神和原则之内,所做的任何修改、等
同替换、改进等,均应包含在本发明的保护范围之内。

一种计算机软件源代码相似度检测方法.pdf_第1页
第1页 / 共28页
一种计算机软件源代码相似度检测方法.pdf_第2页
第2页 / 共28页
一种计算机软件源代码相似度检测方法.pdf_第3页
第3页 / 共28页
点击查看更多>>
资源描述

《一种计算机软件源代码相似度检测方法.pdf》由会员分享,可在线阅读,更多相关《一种计算机软件源代码相似度检测方法.pdf(28页珍藏版)》请在专利查询网上搜索。

本发明涉及一种计算机软件源代码相似度检测方法,属于计算机应用技术领域,该方法包括如下步骤:首先依据编程语言不同,对源代码进行分词操作,然后选择特定标注词对分词结果进行分块处理,依据变量属性对变量分词进行相关处理;而后基于分块结果对各块进行差异测算操作,得到差异矩阵,依据各块差异结果及相关性,得到整体差异;进而根据公式最终得到代码相似度检测结果。对比现有技术,本发明方法能够比较成功的识别代码相似度检。

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 >


copyright@ 2017-2020 zhuanlichaxun.net网站版权所有
经营许可证编号:粤ICP备2021068784号-1