Python实现C代码统计工具(二)
Python实现C代码统计工具(二)
标签: Python 代码统计
声明
本文将对《Python实现C代码统计工具(一)》中的C代码统计工具进行重构,以应对各种使用场景。
一. 问题提出
此前实现的C代码统计工具较为简陋,仅能遍历和分析当前目录及其子目录下的代码文件并输出统计报告。
在实际使用中,可能期望支持同时统计多个目录和(或)文件,并可指定遍历深度。当文件总数较少且文件路径较长时,期望支持显示基本文件名(basename)而非全路径;当文件总数较多时,期望支持仅显示总计信息。为求直观,还应显示代码注释率,即注释行/(注释行+有效代码行)。
二. 代码实现
存储统计结果的rawCountInfo和detailCountInfo结构不变。当然,detailCountInfo也可定义为字典,以文件名为键。
CalcLines()函数也不变。注意,将该函数中的C注释符改为#和''',即可用于Python脚本行数信息统计。
CountFileLines()稍作修改,增加isShortName参数,真则使用基本文件名,假则使用全路径:
def CountFileLines(filePath, isRawReport=True, isShortName=False):
fileExt = os.path.splitext(filePath)
if fileExt[1] != '.c' and fileExt[1] != '.h':
return
try:
fileObj = open(filePath, 'r')
except IOError:
print 'Cannot open file (%s) for reading!', filePath
else:
lineList = fileObj.readlines()
fileObj.close()
if isRawReport:
global rawCountInfo
rawCountInfo[:-1] = [x+y for x,y in zip(rawCountInfo[:-1], CalcLines(lineList))]
rawCountInfo[-1] += 1
elif isShortName:
detailCountInfo.append([os.path.basename(filePath), CalcLines(lineList)])
else:
detailCountInfo.append([filePath, CalcLines(lineList)])
ReportCounterInfo()内增加注释率统计:
def ReportCounterInfo(isRawReport=True):
def SafeDiv(dividend, divisor):
if divisor: return float(dividend)/divisor
elif dividend: return -1
else: return 0
print 'FileLines CodeLines CommentLines EmptyLines CommentPercent %s'\
%(not isRawReport and 'FileName' or '')
if isRawReport:
print '%-11d%-11d%-14d%-12d%-16.2f<Total:%d Code Files>' %(rawCountInfo[0],\
rawCountInfo[1], rawCountInfo[2], rawCountInfo[3], \
SafeDiv(rawCountInfo[2], rawCountInfo[2]+rawCountInfo[1]), rawCountInfo[4])
return
total = [0, 0, 0, 0]
#对detailCountInfo按第一列元素(文件名)排序,以提高输出可读性
detailCountInfo.sort(key=lambda x:x[0])
for item in detailCountInfo:
print '%-11d%-11d%-14d%-12d%-16.2f%s' %(item[1][0], item[1][1], item[1][2], \
item[1][3], SafeDiv(item[1][2], item[1][2]+item[1][1]), item[0])
total[0] += item[1][0]; total[1] += item[1][1]
total[2] += item[1][2]; total[3] += item[1][3]
print '-' * 90 #输出90个负号(minus)或连字号(hyphen)
print '%-11d%-11d%-14d%-12d%-16.2f<Total:%d Code Files>' \
%(total[0], total[1], total[2], total[3], \
SafeDiv(total[2], total[2]+total[1]), len(detailCountInfo))
为支持同时统计多个目录和(或)文件,使用ParseTargetList()解析目录-文件混合列表,将其元素分别存入目录和文件列表:
def ParseTargetList(targetList):
fileList, dirList = [], []
if targetList == []:
targetList.append(os.getcwd())
for item in targetList:
if os.path.isfile(item):
fileList.append(os.path.abspath(item))
elif os.path.isdir(item):
dirList.append(os.path.abspath(item))
else:
print "'%s' is neither a file nor a directory!" %item
return [fileList, dirList]
注意,只有basename的文件或目录默认为当前目录下的文件或子目录。此外,相对路径会被扩展为全路径。
CLineCounter()函数基于目录和文件列表进行统计,并增加isStay参数指定遍历深度(真则当前目录,假则递归其子目录):
def CLineCounter(isStay=False, isRawReport=True, isShortName=False, targetList=[]):
fileList, dirList = ParseTargetList(targetList)
if fileList != []:
CountFile(fileList, isRawReport, isShortName)
elif dirList != []:
CountDir(dirList, isStay, isRawReport, isShortName)
else:
pass
def CountDir(dirList, isStay=False, isRawReport=True, isShortName=False):
for dir in dirList:
if isStay:
for file in os.listdir(dir):
CountFileLines(os.path.join(dir, file), isRawReport, isShortName)
else:
for root, dirs, files in os.walk(dir):
for file in files:
CountFileLines(os.path.join(root, file), isRawReport, isShortName)
def CountFile(fileList, isRawReport=True, isShortName=False):
for file in fileList:
CountFileLines(file, isRawReport, isShortName)
然后,添加命令行解析处理。首选argparse模块解析命令行(Python2.7之后已废弃optparse模块),实现如下:
import argparse
def ParseCmdArgs(argv=sys.argv):
parser = argparse.ArgumentParser(usage='%(prog)s [options] target',
description='Count lines in C code files(c&h).')
parser.add_argument('target', nargs='*',
help='space-separated list of directories AND/OR files')
parser.add_argument('-s', '--stay', action='store_true',
help='do not walk down subdirectories')
parser.add_argument('-d', '--detail', action='store_true',
help='report counting result in detail')
parser.add_argument('-b', '--basename', action='store_true',
help='do not show file\'s full path')
parser.add_argument('-v', '--version', action='version',
version='%(prog)s 2.0 by xywang')
args = parser.parse_args()
return (args.stay, args.detail, args.basename, args.target)
argparse模块默认检查命令行参数sys.argv,也可直接解析字符串。例如,args = parser.parse_args('foo 1 -x 2'.split()),这在调试中很有用。
注意,argparse模块为Python2.7版本新增。在以前的版本中,可使用getopt模块解析命令行。如下所示:
def Usage():
'''
usage: CLineCounter.py [options] target
Count lines in C code files(c&h).
positional arguments:
target space-separated list of directories AND/OR files
optional arguments:
-h, --help show this help message and exit
-s, --stay do not walk down subdirectories
-d, --detail report counting result in detail
-b, --basename do not show file's full path
-v, --version show program's version number and exit'''
print Usage.__doc__
import getopt
def ParseCmdArgs1(argv=sys.argv):
try:
opts, args = getopt.gnu_getopt(argv[1:], 'hsdbv', \
['help', 'stay', 'detail', 'basename', 'version'])
except getopt.GetoptError, e:
print str(e); Usage(); sys.exit()
stay, detail, basename, target = False, False, False, []
verbose = False
for o, a in opts:
if o in ("-h", "--help"):
Usage(); sys.exit()
elif o in ("-s", "--stay"):
stay = True
elif o in ("-d", "--detail"):
detail = True
elif o in ("-b", "--basename"):
basename = True
elif o in ("-v", "--version"):
print '%s 2.0 by xywang' %os.path.basename(argv[0]); sys.exit()
else:
assert False, "unhandled option"
return (stay, detail, basename, args)
其中,Usage()函数输出的帮助信息与argparse模块实现的-h选项输出相同。
建议将命令行处理放入另一文件内,以免python环境不支持argparse时导致代码统计本身不可用。
最后,调用以上函数统计代码并输出报告:
if __name__ == '__main__':
(stay, detail, basename, target) = ParseCmdArgs()
CLineCounter(stay, not detail, basename, target)
ReportCounterInfo(not detail)
三. 效果验证
为验证上节的代码实现,在lctest调试目录下新建subdir子目录。该目录下包含test.c文件。
在作者的Windows XP主机上,以不同的命令行参数运行CLineCounter.py,输出如下:
E:\PyTest>tree /F lctest
E:\PYTEST\LCTEST
│ line.c
│ test.c
│ typec.JPG
│
└─subdir
test.c
E:\PyTest>CLineCounter.py -v
CLineCounter.py 2.0 by xywang
E:\PyTest>CLineCounter.py -d lctest\line.c lctest\test.c
FileLines CodeLines CommentLines EmptyLines CommentPercent FileName
33 19 15 4 0.44 lctest\line.c
44 34 3 7 0.08 lctest\test.c
------------------------------------------------------------------------------------------
77 53 18 11 0.25 <Total:2 Code Files>
E:\PyTest>CLineCounter.py -s -d lctest\subdir\test.c lctest
FileLines CodeLines CommentLines EmptyLines CommentPercent FileName
33 19 15 4 0.44 E:\PyTest\lctest\line.c
44 34 3 7 0.08 E:\PyTest\lctest\test.c
44 34 3 7 0.08 lctest\subdir\test.c
------------------------------------------------------------------------------------------
121 87 21 18 0.19 <Total:3 Code Files>
E:\PyTest>CLineCounter.py -s -d lctest -b
FileLines CodeLines CommentLines EmptyLines CommentPercent FileName
33 19 15 4 0.44 line.c
44 34 3 7 0.08 test.c
------------------------------------------------------------------------------------------
77 53 18 11 0.25 <Total:2 Code Files>
E:\PyTest>CLineCounter.py -d lctest
FileLines CodeLines CommentLines EmptyLines CommentPercent FileName
33 19 15 4 0.44 E:\PyTest\lctest\line.c
44 34 3 7 0.08 E:\PyTest\lctest\subdir\test.c
44 34 3 7 0.08 E:\PyTest\lctest\test.c
------------------------------------------------------------------------------------------
121 87 21 18 0.19 <Total:3 Code Files>
E:\PyTest>CLineCounter.py lctest
FileLines CodeLines CommentLines EmptyLines CommentPercent
121 87 21 18 0.19 <Total:3 Code Files>
E:\PyTest\lctest\subdir>CLineCounter.py -d ..\test.c
FileLines CodeLines CommentLines EmptyLines CommentPercent FileName
44 34 3 7 0.08 E:\PyTest\lctest\test.c
------------------------------------------------------------------------------------------
44 34 3 7 0.08 <Total:1 Code Files>
在作者的Linux Redhat主机(Python 2.4.3)上,运行CLineCounter.py后统计输出如下:
[wangxiaoyuan_@localhost]$ python CLineCounter.py -d lctest
FileLines CodeLines CommentLines EmptyLines CommentPercent FileName
33 19 15 4 0.44 /home/wangxiaoyuan_/lctest/line.c
44 34 3 7 0.08 /home/wangxiaoyuan_/lctest/subdir/test.c
44 34 3 7 0.08 /home/wangxiaoyuan_/lctest/test.c
------------------------------------------------------------------------------------------
121 87 21 18 0.19 <Total:3 Code Files>
[wangxiaoyuan_@localhost subdir]$ python CLineCounter.py -d ../test.c
FileLines CodeLines CommentLines EmptyLines CommentPercent FileName
44 34 3 7 0.08 /home/wangxiaoyuan_/lctest/test.c
------------------------------------------------------------------------------------------
44 34 3 7 0.08 <Total:1 Code Files>
经人工检验,统计信息正确。
此外,可以看出统计输出非常规整。若将其存入Windows文本文件(txt),可方便地转换为Excel文件。以Excel 2007为例,通过Office按钮打开待转换的文本文件,在【文本导入向导】中勾选"分隔符号",点击"完成",将文本文件载入Excel中。此后,即可在Excel界面里自定义修改并另存为Excel文件。
Python实现C代码统计工具(二)的更多相关文章
- Python实现C代码统计工具(三)
目录 Python实现C代码统计工具(三) 声明 一. 性能分析 1.1 分析单条语句 1.2 分析代码片段 1.3 分析整个模块 二. 制作exe Python实现C代码统计工具(三) 标签: Py ...
- Python实现C代码统计工具(四)
目录 Python实现C代码统计工具(四) 标签: Python 计时 持久化 声明 运行测试环境 一. 自定义计时函数 1.1 整个程序计时 1.2 代码片段计时 1.3 单条语句计时 二. 性能优 ...
- Python实现C代码统计工具(一)
目录 Python实现C代码统计工具(一) 声明 一. 问题提出 二. 代码实现 三. 效果验证 四. 后记 Python实现C代码统计工具(一) 标签: Python 代码统计 声明 本文将基于Py ...
- Python代码统计工具
目录 Python代码统计工具 声明 一. 问题提出 二. 代码实现 三. 效果验证 Python代码统计工具 标签: Python 代码统计 声明 本文将对<Python实现C代码统计工具(一 ...
- Python实现代码统计工具——终极加速篇
Python实现代码统计工具--终极加速篇 声明 本文对于先前系列文章中实现的C/Python代码统计工具(CPLineCounter),通过C扩展接口重写核心算法加以优化,并与网上常见的统计工具做对 ...
- python 练习(一)代码统计工具的实现
最近部门成立了一个python学习小组,旨在让大家在做项目中开始成长起来,于是老大就给布置了第一个小任务:代码统计工具,具体的需求如下: 需求: . 能够统计指定目录下C++程序的代码行数. . C+ ...
- C++代码统计工具
自己前几天写的C++代码统计工具. http://pan.baidu.com/s/17SnnH
- svn代码统计工具的金额
StatSVN介绍 StatSVN是Java写开源统计程序,从statCVS从移植.从能Subversion版本号来获取信息库,该项目开发的叙述性说明,然后生成各种表格和图表.例:时间线.针对每一个开 ...
- 代码统计工具-cloc
官网地址:http://cloc.sourceforge.net/ https://sourceforge.NET/projects/cloc/files/ 下载得到cloc-1.64.exe Clo ...
随机推荐
- ScrollView不能包含多个子项,ScrollView can host only one direct child
http://debuglog.iteye.com/blog/1441828 ScrollView不能包含多个子项,ScrollView can host only one direct child ...
- CentOS -- 添加开机自启动 命令 脚本
如果只是添加一条开机启动的命令: 1. chmod +x /etc/rc.d/rc.local 2. 将命令写到 /etc/rc.d/rc.local 这个文件中 3. reboot
- grid - 网格项目跨行或跨列
默认情况下网格项目跨度只有一个列和行,但可以跨越多个行和列. 1.可以通过设置grid-column-end和grid-column-start距离多个网络线号实现多个列跨越. <view cl ...
- Oracle只读用户角色的建立
授予某模式下对象读权限给角色,就可以建立Oracle只读用户角色,下文对该方法的实现步骤作了详细的介绍,供您参考学习. 下面为您介绍的是Oracle只读用户角色的建立方法,该方法供您参考,如果您在Or ...
- Javascript框架的自定义事件(转)
很多 javascript 框架都提供了自定义事件(custom events),例如 jquery.yui 以及 dojo 都支持“document ready”事件.而部分自定义事件是源自回调(c ...
- 基于SIFT特征的全景图像拼接
基于SIFT特征的全景图像拼接 分类: image Machine learning2013-07-05 13:33 2554人阅读 评论(3) 收藏 举报 基于SIFT特征的全景图像拼接 分类: 计 ...
- js 检测变量是否存在
实际开发过程中,会有判断一个变量是否存在的场景 首先想到的是 if(a==undefined){ console.log("a is undefined") }else{ cons ...
- (转)PyCharm报错:“No R interpreter defined: Many R……”——解决办法
报错截图: 下载安装链接:The Comprehensive R Archive Network
- Socket网络编程--聊天程序(2)
上一节简单如何通过Socket创建一个连接,然后进行通信.只是每个人只能说一句话.而且还是必须说完才会接收到信息,总之是很不方便的事情.所以这一小节我们将对上一次的程序进行修改,修改成每个人可以多说话 ...
- N个降序数组,找到最大的K个数
问题定义 这个问题来自网上看到的百度算法题,感觉很不错,问题定义如下: 假定有20个有序数组,每个数组有500个数字,降序排列,数字类型32位uint数值,现在需要取出这10000个数字中最大的500 ...