利用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:
				newStr8 = newStr7
				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对文件的遍历十分方便,这里最主要的就是对正则表达式的使用,下面解释每条正则表达式的作用:

查找:(\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);

}

查找:(//)(.)({)(\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);
这是为了在被消除的代码后面加上的打印,否则会有编译错误

查找:(=+)(\s*)({)(\s*)printf("%s,%d,\S+.(.)\n",FILE,LINE);
替换为:\1\2\3\4
效果:
={printf("%s,%d\n",FILE,LINE);
会被替换成
={
当=后跟随着{表明是赋值操作,打印需要去掉

查找:(.(for|while)\s([^{]{)\sprintf("%s,%d,\S+.(.)\n",FILE,LINE);
替换为:\1
效果:
把while和for循环里的打印语句去掉

查找:(.(enum|extern)(.|\s*){)\sprintf("%s,%d,\S+.(.*)\n",FILE,LINE);
替换为:\1
效果:
去掉枚举和extern里的打印语句

查找:struct(\s*)(.)(\s)({)(\s*)printf("%s,%d,\S+.(.)\n",FILE,LINE)😭\s*)
替换为:struct\1\2\3\4\5
效果:
去掉结构体定义里的打印语句

查找:((")(.[\])"(.*[\])"(.",)
替换为:\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++的情况上面应该可以覆盖

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值