Python实现C代码统计工具(二)

标签: Python 代码统计


声明

本文将对《Python实现C代码统计工具(一)》中的C代码统计工具进行重构,以应对各种使用场景。

一. 问题提出

此前实现的C代码统计工具较为简陋,仅能遍历和分析当前目录及其子目录下的代码文件并输出统计报告。

在实际使用中,可能期望支持同时统计多个目录和(或)文件,并可指定遍历深度。当文件总数较少且文件路径较长时,期望支持显示基本文件名(basename)而非全路径;当文件总数较多时,期望支持仅显示总计信息。为求直观,还应显示代码注释率,即注释行/(注释行+有效代码行)。

二. 代码实现

存储统计结果的rawCountInfodetailCountInfo结构不变。当然,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代码统计工具(二)的更多相关文章

  1. Python实现C代码统计工具(三)

    目录 Python实现C代码统计工具(三) 声明 一. 性能分析 1.1 分析单条语句 1.2 分析代码片段 1.3 分析整个模块 二. 制作exe Python实现C代码统计工具(三) 标签: Py ...

  2. Python实现C代码统计工具(四)

    目录 Python实现C代码统计工具(四) 标签: Python 计时 持久化 声明 运行测试环境 一. 自定义计时函数 1.1 整个程序计时 1.2 代码片段计时 1.3 单条语句计时 二. 性能优 ...

  3. Python实现C代码统计工具(一)

    目录 Python实现C代码统计工具(一) 声明 一. 问题提出 二. 代码实现 三. 效果验证 四. 后记 Python实现C代码统计工具(一) 标签: Python 代码统计 声明 本文将基于Py ...

  4. Python代码统计工具

    目录 Python代码统计工具 声明 一. 问题提出 二. 代码实现 三. 效果验证 Python代码统计工具 标签: Python 代码统计 声明 本文将对<Python实现C代码统计工具(一 ...

  5. Python实现代码统计工具——终极加速篇

    Python实现代码统计工具--终极加速篇 声明 本文对于先前系列文章中实现的C/Python代码统计工具(CPLineCounter),通过C扩展接口重写核心算法加以优化,并与网上常见的统计工具做对 ...

  6. python 练习(一)代码统计工具的实现

    最近部门成立了一个python学习小组,旨在让大家在做项目中开始成长起来,于是老大就给布置了第一个小任务:代码统计工具,具体的需求如下: 需求: . 能够统计指定目录下C++程序的代码行数. . C+ ...

  7. C++代码统计工具

    自己前几天写的C++代码统计工具. http://pan.baidu.com/s/17SnnH

  8. svn代码统计工具的金额

    StatSVN介绍 StatSVN是Java写开源统计程序,从statCVS从移植.从能Subversion版本号来获取信息库,该项目开发的叙述性说明,然后生成各种表格和图表.例:时间线.针对每一个开 ...

  9. 代码统计工具-cloc

    官网地址:http://cloc.sourceforge.net/ https://sourceforge.NET/projects/cloc/files/ 下载得到cloc-1.64.exe Clo ...

随机推荐

  1. 17、python对内存的使用

    python对内存的使用 浅拷贝和深拷贝 所谓浅拷贝就是对引用的拷贝(只拷贝父对象) 所谓深拷贝就是对对象的资源的拷贝 解释一个例子: import copy a = [1,2,3,['a','b', ...

  2. UVa 902 - Password Search

    题目:给你一个小写字母组成大的串和一个整数n.找到里面长度为n出现最频繁的子串. 分析:字符串.hash表.字典树. 这里使用hash函数求解,仅仅做一次扫描就可以. 说明:假设频率同样输出字典序最小 ...

  3. 微软BI 之SSAS 系列 - 多维数据集维度用法之三 多对多维度 Many to Many

    开篇介绍 对于维度成员和事实数据直接的关系看到更多的可能还是一对一,一对多的关系.比方在事实维度(或退化维度)中一个订单和明细号组合而成的ID,对应的就是事实表中的一条数据,这就是一对一的关系.比方说 ...

  4. MySQL5.7多主一从(多源复制)同步配置

    MySQL5.7多主一从(多源复制)同步配置(抄袭) 原文地址:https://my.oschina.net/u/2399373/blog/2878650 多主一从,也称为多源复制,数据流向: 主库1 ...

  5. JS代码把JSON字符串转换为对象,计算对象的长度并把它转换为数字类型,把转换的值相加减

    Number(JSON.parse(rowObject.RenewalProperty).length)-1

  6. Mysql INSERT、REPLACE、UPDATE的区别

    用于操作数据库的SQL一般分为两种,一种是查询语句,也就是我们所说的SELECT语句,另外一种就是更新语句,也叫做数据操作语句.言外之意,就是对数据进行修改.在标准的SQL中有3个语句,它们是INSE ...

  7. Duplicate Manager Pro for Mac(重复文件查找工具)破解版安装

    1.软件简介    Duplicate Manager Pro 是 macOS 系统上一款重复文件查找工具,可以帮你在 Mac 电脑上查找出磁盘上面的重复文件,然后让你对这些重复文件进行判断并删除,使 ...

  8. [Aaronyang] 写给自己的WPF4.5 笔记9[复杂数据处理三步曲,数据展示ListView泪奔2/3]

     我的文章一定要做到对读者负责,否则就是失败的文章  ---------   www.ayjs.net    aaronyang技术分享 作者留言:        小小的推荐,作者的肯定,读者的支持. ...

  9. Hyper-V 怎样拷贝文件至虚拟硬盘并附加到虚拟机上

    对于大文件来说,通过远程桌面拷贝是件麻烦的事情,虽然简单,但速度受限太多,不推荐使用. 我工作中对于大文件的拷贝,通过创建一个新的虚拟硬盘(VHD),再把大文件拷贝至虚拟硬盘中,最后附加到虚拟机上. ...

  10. 【iCore4 双核心板_FPGA】例程十六:基于双口RAM的ARM+FPGA数据存取实验

    实验现象: 核心代码: int main(void) { /* USER CODE BEGIN 1 */ int i; int address,data; ; ]; ]; char *p; /* US ...