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 ...
随机推荐
- uva 10816 Travel in Desert(简单的好题~两种方法)
题意: 给出 一个图 点与点之间的路径上有两个权值 路径长度和温度 要求在所走路径中的温度的最大值最小的前提下 走最短路径 解题思路1: 首先用 最小生成树 的方法走出 最小瓶颈路 .把在这期间用到的 ...
- HDOJ 1770 - 阅读理解...树形DP
题意: 一个能量E可以通过吸收某个光子的能量变成E1或者释放某个光子的能量变成E2...并且任意两个能量的转化路径至多一条...现在有一堆能量,有一堆光子...如果某个能量与某个光子做直接运算(加上其 ...
- Js 判断浏览器类型整理
判断原理 JavaScript是前端开发的主要语言,我们可以通过 编写JavaScript程序来判断浏览器的类型及版本.JavaScript判断浏览器类型一般有两种办法,一种是根据各种浏览器独有的属性 ...
- The correct way to initialize a dynamic pointer to a multidimensional array
From:https://stackoverflow.com/questions/18273370/the-correct-way-to-initialize-a-dynamic-pointer-to ...
- Eclipse 4.5.0 离线安装 Veloeclipse 插件
下载 Veloeclipse 在 Eclipse eclipse-jee-mars-R-win32-x86_64 版本 4.5.0,Build id 为 20150621-1200,离线安装 Velo ...
- git的使用笔记
1.git下载:https://git-scm.com/downloads 安装git 2.在github.com网站上注册账号 网址:https://github.com/ 3.使用gi ...
- 高性能Javascript(2) DOM编程
第三部分 DOM编程 文档对象模型(DOM)是一个独立于语言的,使用XML和HTML文档操作的应用程序接口(API).在浏览器中,主要与HTML文档打交道,在网页应用中检索XML文档也很常见.DOM ...
- JavaScript中的namespace
<head> <title> New Document </title> <script> var global = window.global||{} ...
- OpenCV 学习笔记 06 图像检索以及基于图像描述符的搜索
OpenCV 可以检测图像的主要特征,然后提取这些特征,使其成为图像描述符,这些图像特征可作为图像搜索的数据库:此外可以利用关键点将图像拼接 stitch 起来,组成一个更大的图像.如将各照片组成一个 ...
- 2013-2015 Aaronyang的又一总结,牧童遥指纳尼村
我没有时间去唠叨自己的事,可是你们是我喜欢的人,ay很愿意写给你们分享:去年的万人阅读的总结链接:<没学历的IT人生没那么悲催,献给程序员们> 提前声明:本文不良反应:请自备垃圾桶,准备装 ...