1. 程式人生 > >利用python實現批量插入列印資訊的方法

利用python實現批量插入列印資訊的方法

使用列印資訊是除錯程式的必備手段,但是面對下面兩種情況:
1.如果程式程式碼/原始檔較多,而一時之間又無法確定問題範圍,這個時候可能需要在多個檔案插入列印資訊
2.剛剛接手維護大型模組,想要了解執行流程,需要在有可能執行到的地方都加入trace

上述情況,如果手動在每個函式的開頭,每個判斷語句都加入trace,會耗費很多時間
python是處理資料的一個好工具,結合正則表示式,我們可以寫一個指令碼,實現:
在每個函式/每個判斷的入口列印函式名/判斷的內容,當然前提是必須是在{}內
同時,我們要避免在迴圈語句內列印

下面是指令碼:

import re
import os
import
os.path rootpath = ''#程式碼所在的路徑 filterList = [];#要過濾的檔案 needFilter = False for parent, dirs, files in os.walk(rootpath): for f in files: for idx in range(len(filterList)): if f == filterList[idx]: needFilter = True break if needFilter == True
: needFilter = False continue fname = os.path.splitext(f)#將檔名和字尾名分離 if fname[1] == '.c' or fname[1] == '.cpp': fd = open(os.path.join(parent,f)) code = fd.read() fd.close() newStr1 = re.sub(r'(\S+)(.*)(\()(.*)(\))(\s*)([^\{|\}|\(|\)]*)(\s*)(\{)(?!\})(\s*)'
,r'\1\2\3\4\5\6\7\8\9\10printf("%s,%d,\1\2\3\4\5\\n",__FILE__,__LINE__);\10',code) newStr2 = re.sub(r'(//)(.*)(\{)(\s*)printf\("%s,%d,\S+.*\(.*\)\\n",__FILE__,__LINE__\);',r'\1\2\3\4\1printf("%s,%d,\S+.*\(.*\)\\n",__FILE__,__LINE__);',newStr1) newStr3 = re.sub(r'(=+)(\s*)(\{)(\s*)printf\("%s,%d,\S+.*\(.*\)\\n",__FILE__,__LINE__\);',r'\1\2\3\4',newStr2) newStr4 = re.sub(r'(.*(for|while)\s*\([^\{]*\{)\s*printf\("%s,%d,\S+.*\(.*\)\\n",__FILE__,__LINE__\);',r'\1',newStr3) newStr5 = re.sub(r'(.*(enum|extern)(.*|\s*)\{)\s*printf\("%s,%d,\S+.*\(.*\)\\n",__FILE__,__LINE__\);',r'\1',newStr4) newStr6 = re.sub(r'struct(\s*)(.*)(\s*)(\{)(\s*)printf\("%s,%d,\S+.*\(.*\)\\n",__FILE__,__LINE__\);(\s*)',r'struct\1\2\3\4\5',newStr5) newStr7 = re.sub(r'(\(\")(.*[^\\])\"(.*[^\\])\"(.*\",)',r'\1\2\\"\3\\"\4',newStr6) newStr8 = newStr7 while True: newStr7 = re.sub(r'(\(\")(.*[^\\])\"(.*[^\\])\"(.*\",)',r'\1\2\\"\3\\"\4',newStr7) if newStr8 == newStr7: break newStr8 = newStr7 fd=file(os.path.join(parent,f),"w+") fd.write(newStr8) fd.close()

執行這個指令碼後,會遍歷rootpath下的所有檔案(包括子目錄下的檔案),然後過濾掉我們指定的檔案,要過濾的檔案放在 filterList 裡,過濾掉後,在剩下的檔案裡,篩選出C/C++原始檔,對每個篩選出來的檔案,進行以下操作:
1.從檔案讀取出程式碼
2.利用正則表示式新增trace
3.將程式碼寫回檔案

python對檔案的遍歷十分方便,這裡最主要的就是對正則表示式的使用,下面解釋每條正則表示式的作用:

1.
查詢:(\S+)(.)(()(.)())(\s*)([^{|}|(|)])(\s)({)(?!})(\s*)
替換為:\1\2\3\4\5\6\7\8\9\10printf(“%s,%d,\1\2\3\4\5\n”,FILE,LINE);\10
效果:
下面的程式碼
{

}
會被替換成
{
printf(“%s,%d\n”,FILE,LINE);

}

2.
查詢:(//)(.)({)(\s)printf(“%s,%d,\S+.(.)\n”,FILE,LINE);
替換為:\1\2\3\4\1printf(“%s,%d,\S+.(.)\n”,FILE,LINE);
效果:
下面的程式碼
//…{
printf(“%s,%d\n”,FILE,LINE);
會被替換成
//…{
// printf(“%s,%d\n”,FILE,LINE);
這是為了在被消除的程式碼後面加上的列印,否則會有編譯錯誤

3.
查詢:(=+)(\s*)({)(\s*)printf(“%s,%d,\S+.(.)\n”,FILE,LINE);
替換為:\1\2\3\4
效果:
={printf(“%s,%d\n”,FILE,LINE);
會被替換成
={
當=後跟隨著{表明是賦值操作,列印需要去掉

4.
查詢:(.(for|while)\s([^{]{)\s*printf(“%s,%d,\S+.(.*)\n”,FILE,LINE);
替換為:\1
效果:
把while和for迴圈裡的列印語句去掉

5.
查詢:(.(enum|extern)(.|\s*){)\s*printf(“%s,%d,\S+.(.)\n”,FILE,LINE);
替換為:\1
效果:
去掉列舉和extern裡的列印語句

6.
查詢:struct(\s*)(.)(\s)({)(\s*)printf(“%s,%d,\S+.(.)\n”,FILE,LINE);(\s*)
替換為:struct\1\2\3\4\5
效果:
去掉結構體定義裡的列印語句

7.
查詢:((\”)(.[^\])\”(.[^\])\”(.*\”,)
替換為:\1\2\”\3\”\4
效果:
因為插入的列印語句會把{前的一行內容打印出來,考慮下面的情況
if(a == “test”)
插入列印語句後
if(a == “test”){
printf(“%s,%d,a == “test”\n”,FILE,LINE);
由於”“沒有被轉義,所以編譯會出錯,上面的正則替換就是為了增加轉義,增加後:
if(a == “test”){
printf(“%s,%d,a == \”test\”\n”,FILE,LINE);
使用迴圈是為了應對修改的資料裡有多組”“,當處理結果和上次得到的結果一樣時,說明每對”“都已經被處理完,加上了轉移字元

如果發現自己的程式碼還有特殊的情況要處理,可以利用正則表示式繼續處理,一般C/C++的情況上面應該可以覆蓋