通过全文相似度来寻找相同或相似的代码

最近笔者在职的公司在不断的做App的包瘦身工作, 身边的同事们也研究出了各种各样实用的工具来辅助加快包瘦身的进程。在这么一个大环境下, 笔者突然又冒出一个很无聊的工具想法

通过文本匹配来寻找相似的方法函数

笔者给这个小工具取了一个非常传神且牛逼的名字 - SameCodeFinder

和上一个查找Block的无聊的小工具RiskBlockScanner类似, 这个工具笔者觉得也是一个应用面相对比较小的一个工具, 所以笔者自嘲无聊的小工具哈~

笔者自认为这个是一个无聊的小工具, 但是还是坚持把它开发出来了, 因为笔者坚信:

任何一个无聊小众的作品, 在合适的时机总是能够帮助到合适的人的!

笔者开发这个小工具除了因为笔者相信这个工具肯定能够帮助到部分人群以外, 还有另外一个目的是督促自己不要停止学习的步伐哈~

起源-辅助研发自查

查找相似代码想法的起源是因为笔者在在职的公司项目处于包瘦身的大环境下。在这个大环境下, 笔者身边的一名同事发明了基于otoollinkmap分析查找无用方法的工具, 该工具在Github上有个类似的开源脚本项目objc_cover。与此同时, 笔者的另外一名同事发表了一种基于Clang来查找无用方法的博文

受这两位同事的影响, 笔者就在想自己能搞什么和他们不一样点的工具么。因为笔者之前用文本扫描的方式搞了一个简易的快速Block检查脚本, 笔者就在想能不能通过类似的手段写一个类似的程序。笔者想借鉴《基于Clang来查找无用方法》的思路进行扩展, 因为该文章里提出了一种将文本内容Hash后进行内容比较, 判断方法是否完全重合的思路

笔者基于该思路进行扩展, 设想能不能不止比较“完全相同”的方法, 还能比较相似的方法。顺着这个思路发现了Google的全文搜索相似度比较的一种算法simhash[7, 8]。

Simhash

关于simhash的介绍引用博文《simhash算法原理及实现》 里的介绍

simhash是google用来处理海量文本去重的算法。 google出品,你懂的。 simhash最牛逼的一点就是将一个文档,最后转换成一个64位的字节,暂且称之为特征字,然后判断重复只需要判断他们的特征字的距离是不是<n(根据经验这个n一般取值为3),就可以判断两个文档是否相似。

上述引用其实有点不完全正确, simhash貌似并不是Google出品的, 第一作者的邮箱后缀明明是普林斯顿大学好不好~ 不过Google将其应用到了网络爬虫并发表了一篇文章哈~

PS: 想了解详情? 阅读Paper去... =。=

《simhash算法原理及实现》 针对simhash梳理了简易的原理介绍以及使用判断距离的汉明距离, 可以便于读者快速了解, 但是如果大家想要了解更深层次的实现, 可以去阅读原版paper《Detecting Near-Duplicates For Web Crawling》《Similarity estimation techniques from
rounding algorithms》

原理简析

simhash的生产步骤可以分为如下:

  1. 提取目标文本的关键字feature和权重weight, 并成对存储
    • 如果不知道怎么提取的同学, 可能需要稍微了解全文搜索相关的知识
  2. 将提取出来的关键字进行传统Hash, 输出二进制的值
  3. 将每一个关键字提取的Hash按位进行运算, 如果当前位是1, 则增加对应的权重; 如果当前位是0, 增减少当前对应的权重;
  4. 将最后得出来的hash值, 如果大于等于1, 则当做1处理; 负数和0当做0处理, 得出最终的二进制值

上述步骤可以简化为下图, 此图引用了我的数学之美系列二 —— simhash与重复信息识别中的图

simhash原理示意

汉明距离

simhash是一种局部敏感Hash。因此可以利用汉明距离去衡量simhash的相似度。

引入Wikipedia的汉明距离介绍:

In information theory, the Hamming distance between two strings of equal length is the number of positions at which the corresponding symbols are different.

字面上意思好像就是两个字符串在不一样字符个数的数量, 在我们现在的应用场景就是统计1或者0的个数, 然后他们的个数差就是距离了。。。一般搜索引擎的历史经验默认是3

PS: 别问我怎么知道的3的, 我也是从博客里看来的, 没有数据依据

寻找相似的代码

寻找完全相似的文件

针对上述理论, 只要是一个文档都可以计算出两者的汉明距离, 利用汉明距离来就可以衡量两个文档的相似度了。笔者在这里目前没有做太多的工作, 只不过过滤了文档的后缀, 让相当类型的文档进行相互的比较。

寻找相似的文件和寻找相似的代码文件, 其实本质上差距不大。代码文件有一些特性, 例如前面的声明和引用都有一列类似的地方, 如果在进行simhash计算处理前能够提前对代码文件进行预处理的话, 能够大幅度的提高整个代码文件相似度计算的精度。

PS: 鉴于思路的完善性和时间成本, 笔者还没有针对代码进行预处理

寻找雷同方法函数

既然利用simhash以及汉明距离可以计算两个文档的相似度, 然自然可以缩小范围计算两个函数方法的相似度。那么问题的关键就在于怎么样才能提取到合适正确的方法函数内容

笔者目前使用的是文本扫描匹配的方式, 但是笔者的同事有提出一种是基于clang插件来提取编译器预处理之后的内容进行hash比较的可行思路。无奈鉴于实现成本和插件无法独立运行的方面考虑, 暂时采用的直接扫描匹配文本的方式进行比较。

目前笔者采取的提取方法体方法是:

  1. 用正则匹配获取方法起始行
  2. 从起始行开始记录左右括号的格式, 并且将起始行开始的所有字符串记录
  3. 当左右括号的个数相互抵消的时候默认当做匹配整个方法, 保存整个字符串

鉴于方法匹配需要根据语法实现, 所以目前只能根据每个语言的语法特性进行截获, 目前SameCodeFinder仅支持Object-C和Java。

语法特性局限了脚本的可扩展性, 步骤一的正则需要和后缀匹配, 步骤二的左右括号在某些语言下不适用, 只能利用发现下一个方法起始行作为步骤三的结束步骤。

Java目前采用正则:

ur"(public|private)(.*)\)\s?{"

Object-C目前采用正则:

ur"(\-|\+)\s?\(.*\).*(\:\s?\(.*\).*)?{?"

排序

无论是寻找雷同的文件还是寻找雷同的方法, 最后计算出的Hash结果都是N * N个的, 那么怎么展示计算的结果呢? 如果把所有的结果都展示出来, 那明显可阅读性太低。

目前采用的逻辑是:

  1. N * N 中第一个N只找出距离最小的第一个返回, 这样过滤结果只保留N个
  2. 将第一步过滤返回的N个结果按照从小到大的方式进行排序

此外,在执行排序的步骤1和步骤2之间, 都可以添加一个最大距离过滤, 默认不超过20, 可以大幅度减少步骤1和步骤2的计算排序过滤时间。

开源实现

笔者基于上述思路以及现成的工具, 利用python脚本花了2天时间去高速实现了一个简易的python脚本, 并开源到了Github上。

访问地址: SameCodeFinder

目前开源的版本可能因为笔者使用不当或者开源python版本的simhash的计算太过耗时, 因此在性能上存在一定的性能问题, 计算整个较大的工程需要花费不少的时间(计算一个大型工程是分钟级别的)。

笔者会在之后寻找突破方法来提高这方面的计算性能~

总结

SameCodeFinder可以帮助大家寻找相似或者完全重叠的方法以及类, 极大程度上可以辅助大家寻找可以复用的代码。SameCodeFinder的实现思路都是借用Google的全文相似度比较的现成实现, 并没有什么创新, 但是脚本化和针对语种设计的方法识别, 能够帮助大家节省不少直接利用simhash去实现的成本。

PS: 个人水平有限, 有错误之处请大家及时指出, 随时交流哈~

参考文献

  1. CLANG技术分享系列四:IOS APP无用代码/重复代码分析
  2. A Python Implementation of Simhash Algorithm
  3. otool
  4. Mac的反编译工具一:otool (objdump工具的OSX对应工具)
  5. iOS APP可执行文件的组成
  6. simhash算法原理及实现
  7. GS Manku, A Jain, A Das Sarma. Detecting Near-Duplicates For Web Crawling. International Conference on World Wide Web. International Conference on World Wide Web. 141-149. 2007.
  8. M. Charikar. Similarity estimation techniques from
    rounding algorithms. In Proc. 34th Annual Symposium
    on Theory of Computing (STOC 2002), pages
    380–388, 2002.
  9. 我的数学之美系列二 —— simhash与重复信息识别
  10. Locality-sensitive hashing
  11. Hamming_distance
  12. Mach-O Executables
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容