利用python实现批量插入打印信息的方法

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/keheinash/article/details/50867338

使用打印信息是调试程序的必备手段,但是面对下面两种情况:
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++的情况上面应该可以覆盖

猜你喜欢

转载自blog.csdn.net/keheinash/article/details/50867338