Page 330 - 《软件学报》2021年第7期
P. 330
2248 Journal of Software 软件学报 Vol.32, No.7, July 2021
在共计达 519 万行的结果中,仅 37 735 行的统计次数达到 20 次及以上,仅 10 473 行统计次数达 50 次及以
上,而 100 次以上的行数仅有 4 679 行,可见在实际工程项目中频繁出现的行集中于极少一部分行内容上,这些
行往往是与代码中一些特殊语义结构相关的.因此,只要消除了这些常见行所带来的负面影响,即筛除这些常见
行而保留那些罕见且更能体现源代码功能特性的行,就能够解决大部分现存的误报,大幅度地提高算法可用性,
本文第 4 节的实验结果也证实了这一点.
Table 1 Common lines in code files
表 1 常见重复行
排序 行内容 出现次数
1 }else{ 74 960
2 break; 48 733
3 @Override 42 073
4 break 29 730
5 return; 24 601
6 try{ 22 353
7 returnfalse; 21 238
8 return 15 586
同时,可以注意到,如果将所有语言的代码文件一起进行常见行统计,其结果是不够合理的.这些影响结果
的常见行往往跟语言相关联,不同语言的常见行差异非常显著,因此不能采用统一的筛选器对所有语言的源代
码进行行筛选.对此,本文选取了 10 种常用语言(c#、c/c++、go、java、js、php、python、ruby、sql、swift),并
对每种语言分别进行常用行统计.数据源选自 Github 星数排名前 50 000 的开源项目,将其中每种语言的源代码
文件进行预处理后按出现频数降序排列,其中部分语言的常见行统计到前 15 名,结果见表 2.从表 2 可见,在代码
文件中频繁出现的行不仅包含纯符号行,也包含一些特殊的与语义相关的行,这些行在代码中的频繁出现并不
能直接地体现代码功能,包含的语义信息也较少,因此并不属于第 2.2 节中提到的频繁出现一些功能性行内容
的行覆盖情况.若仅通过纯符号行对结果进行筛选,其余频繁出现且特性体现较差的行就会影响指纹特征的提
取,比重较大时将出现行覆盖的情况,严重影响结果的精确度.同时,尽管不同语言之间有一些通用的常见行,如
“returnfalse;”“}else{”“return”等,但是由于不同语言的语法特点不同,其常见行列表之间差异性也较大,存在一些
语言特有的常见行,如 python 中的“pass”、ruby 中的“ensure”等.
Table 2 Common code lines in different languages
表 2 部分语言常见行统计结果
语言 行内容 语言 行内容 语言 行内容 语言 行内容
} } else: end
@Override { ) else
{ <?php try: }
@test ); } )
try{ ), }, beforedo
}else{ ], pass begin
break; }else{ ] {
JAVA returnfalse; PHP array( python ), ruby },
returnthis; ]; return private
returnnull; break; @property ]
}); [ continue includeaws::structure
returntrue; returnfalse; }), super
return; return$this; returnfalse [
importjava.util.list; ] }], ensure
}; ) break require'spec_helper'
如图 3 所示,左右两边的代码分别节选自不同工程的两个文件,从语义和功能的角度来看,两者截然不同.根
据现有的相似哈希计算方法,两个文件的每一行都将作为一个特征来共同计算指纹值,由于两边均多次出现
“#endif”行,导致最终计算出来的相似哈希指纹值的海明距离为 7.可以看到,两边多次出现的“#endif”行是对区
分完全没有帮助的,真正能够体现程序具体特性的是剩下的那些行.因此,与 TF-IDF 方法不同,本文提出的算法