通過全文相似度來尋找相同或相似的代碼
最近筆者在職的公司在不斷的做App的包瘦身工作, 身邊的同事們也研究出了各種各樣實用的工具來輔助加快包瘦身的進程。在這么一個大環境下, 筆者突然又冒出一個很無聊的工具想法
通過文本匹配來尋找相似的方法函數
筆者給這個小工具取了一個非常傳神且牛逼的名字 - SameCodeFinder
和上一個查找Block的無聊的小工具RiskBlockScanner類似, 這個工具筆者覺得也是一個應用面相對比較小的一個工具, 所以筆者自嘲無聊的小工具哈~
筆者自認為這個是一個無聊的小工具, 但是還是堅持把它開發出來了, 因為筆者堅信:
任何一個無聊小眾的作品, 在合適的時機總是能夠幫助到合適的人的!
筆者開發這個小工具除了因為筆者相信這個工具肯定能夠幫助到部分人群以外, 還有另外一個目的是督促自己不要停止學習的步伐哈~
起源-輔助研發自查
查找相似代碼想法的起源是因為筆者在在職的公司項目處于包瘦身的大環境下。在這個大環境下, 筆者身邊的一名同事發明了基于otool和linkmap分析查找無用方法的工具, 該工具在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的生產步驟可以分為如下:
提取目標文本的關鍵字feature和權重weight, 并成對存儲
如果不知道怎么提取的同學, 可能需要稍微了解全文搜索相關的知識
將提取出來的關鍵字進行傳統Hash, 輸出二進制的值
將每一個關鍵字提取的Hash按位進行運算, 如果當前位是1, 則增加對應的權重; 如果當前位是0, 增減少當前對應的權重;
將最后得出來的hash值, 如果大于等于1, 則當做1處理; 負數和0當做0處理, 得出最終的二進制值
上述步驟可以簡化為下圖, 此圖引用了我的數學之美系列二 —— 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比較的可行思路。無奈鑒于實現成本和插件無法獨立運行的方面考慮, 暫時采用的直接掃描匹配文本的方式進行比較。
目前筆者采取的提取方法體方法是:
用正則匹配獲取方法起始行
從起始行開始記錄左右括號的格式, 并且將起始行開始的所有字符串記錄
當左右括號的個數相互抵消的時候默認當做匹配整個方法, 保存整個字符串
鑒于方法匹配需要根據語法實現, 所以目前只能根據每個語言的語法特性進行截獲, 目前SameCodeFinder)僅支持Object-C和Java。
語法特性局限了腳本的可擴展性, 步驟一的正則需要和后綴匹配, 步驟二的左右括號在某些語言下不適用, 只能利用發現下一個方法起始行作為步驟三的結束步驟。
Java目前采用正則:
1
ur"(public|private)(.*)\)\s?{"
Object-C目前采用正則:
1
ur"(\-|\+)\s?\(.*\).*(\:\s?\(.*\).*)?{?"
排序
無論是尋找雷同的文件還是尋找雷同的方法, 最后計算出的Hash結果都是N * N個的, 那么怎么展示計算的結果呢? 如果把所有的結果都展示出來, 那明顯可閱讀性太低。
目前采用的邏輯是:
N * N 中第一個N只找出距離最小的第一個返回, 這樣過濾結果只保留N個
將第一步過濾返回的N個結果按照從小到大的方式進行排序
此外,在執行排序的步驟1和步驟2之間, 都可以添加一個最大距離過濾, 默認不超過20, 可以大幅度減少步驟1和步驟2的計算排序過濾時間。
開源實現
筆者基于上述思路以及現成的工具, 利用python腳本花了2天時間去高速實現了一個簡易的python腳本, 并開源到了Github上。
訪問地址: SameCodeFinder
目前開源的版本可能因為筆者使用不當或者開源python版本的simhash的計算太過耗時, 因此在性能上存在一定的性能問題, 計算整個較大的工程需要花費不少的時間(計算一個大型工程是分鐘級別的)。
筆者會在之后尋找突破方法來提高這方面的計算性能~
總結
SameCodeFinder可以幫助大家尋找相似或者完全重疊的方法以及類, 極大程度上可以輔助大家尋找可以復用的代碼。SameCodeFinder的實現思路都是借用Google的全文相似度比較的現成實現, 并沒有什么創新, 但是腳本化和針對語種設計的方法識別, 能夠幫助大家節省不少直接利用simhash去實現的成本。
PS: 個人水平有限, 有錯誤之處請大家及時指出, 隨時交流哈~
參考文獻
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.
M. Charikar. Similarity estimation techniques from
rounding algorithms. In Proc. 34th Annual Symposium
on Theory of Computing (STOC 2002), pages
380–388, 2002.
掃描二維碼推送至手機訪問。
版權聲明:本文由短鏈接發布,如需轉載請注明出處。
本文鏈接:http://www.virginiabusinesslawupdate.com/article_288.html