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. Standard Series Values in a Decade for Resistances and Capacitances E24 E48 E96

    E3     50% tolerance (no longer used)E6     20% tolerance (now seldom used)E12   10% toleranceE24    ...

  2. [YARN] 2.2 GB of 2.1 GB virtual memory used. Killing container.

    Spark程序在yarn的集群运行,出现 Current usage: 105.9 MB of 1 GB physical memory used; 2.2 GB of 2.1 GB virtual ...

  3. [Android Pro] Android P版本 新功能介绍和兼容性处理(三)Android Studio 3.0 ~ 3.2 其他特性

    cp : https://blog.csdn.net/yi_master/article/details/80067198 1:JAVA8特性支持 1)Base64.java 在升级到as3.0之后, ...

  4. Java ThreadLocal的使用

    Java中的ThreadLocal类允许我们创建只能被同一个线程读写的变量.因此,如果一段代码含有一个ThreadLocal变量的引用,即使两个线程同时执行这段代码,它们也无法访问到对方的Thread ...

  5. 当echarts的legend字数过多的时候变成省略号

    legend: { data: ['国有土地使用','食品药品安全','生态环境和资源保护','国有财产保护'], orient: 'horizontal', left: '10', bottom:' ...

  6. 【转】JavaScript 的装载和执行

    承接前面一篇文章<浏览器的渲染原理简介> ,本文来说下JavaScript的装载和执行. 通常来说,浏览器对于 JavaScript 的运行有两大特性: 1) 载入后马上执行 2) 执行时 ...

  7. 【jQuery Demo】图片瀑布流实现

    瀑布流就是像瀑布一样的网站——丰富的网站内容,特别是绚美的图片会让你流连忘返.你在浏览网站的时候只需要轻轻滑动一下鼠标滚轮,一切的美妙的图片精彩便可呈现在你面前.瀑布流网站是新兴的一种网站模式——她的 ...

  8. 使用SpringBoot Admin监控SpringCloud微服务

    spring-boot admin的github地址:https://github.com/codecentric/spring-boot-admin 本文基于SpringCloud的环境和配置上增加 ...

  9. dup2替换

    今天看APUE上一道题,要求不能用fcnt1来替换dup1. 刚开始的思路是dup一个,测试发现与期望的不一致就马上关闭,发现遇到无限循环,刚才想了下,才发现一旦close掉,再次dup仍然是分配最小 ...

  10. Spring全局异常处理的三种方式

    在J2EE项目的开发中,不管是对底层的数据库操作过程,还是业务层的处理过程,还是控制层的处理过程,都不可避免会遇到各种可预知的.不可预知的异常需要处理.每个过程都单独处理异常,系统的代码耦合度高,工作 ...