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. centos 6.4配置samba+ldap认证

    原文地址:http://www.centoscn.com/image-text/config/2015/0716/5866.html  1. 什么是samba Samba服务类似于windows上的共 ...

  2. Android导入第三方静态库.a编译成动态库.so

    http://ikinglai.blog.51cto.com/6220785/1324985 在Android开发的时候,经常会使用到用c或c++编写的第三方的静态库.如果有源码的话,可以直接跟你自己 ...

  3. iOS开发-观察者模式

    观察者模式也被称作发布/订阅模式,观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象.这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己.观察者模式中 ...

  4. 添加script标签、添加事件

    添加script标签 var _hmt = _hmt || [];    (function () {        var hm = document.createElement("scr ...

  5. eclipse core expression usage

    http://codeandme.blogspot.com/2012/04/expression-examples.html We need to set checkEnabled on the vi ...

  6. 12C新特性--Application Continuity

    Application Continuity特性可以在中断后恢复受影响的数据库会话的任务,从而让终端用户和应用程序感觉不到中断的发生.Application Continuity执行恢复的过程是在应用 ...

  7. linux内核剖析(零)linux系统启动过程详解-开机加电后发生了什么

    本文参考了如下文章 深入理解linux启动过程 mbr (主引导记录(Master Boot Record)) 电脑从开机加电到操作系统main函数之前执行的过程 详解linux系统的启动过程及系统初 ...

  8. curl 调用jenkins的api

    jenkins提供了rest api,通过调用接口,可以执行一些job的操作,如构建job ,新建job,启用禁用等操作骑车 其次curl是什么? cURL是一个利用URL语法在命令行下工作的文件传输 ...

  9. Spring Boot 2.0 入门指南

    0x01 什么是Spring Boot? Spring Boot是用来简化Spring应用初始搭建以及开发过程的全新框架,被认为是Spring MVC的“接班人”,和微服务紧密联系在一起. 0x02 ...

  10. Spring Boot 2.0 利用 Spring Security 实现简单的OAuth2.0认证方式2

    0.前言 经过前面一小节已经基本配置好了基于SpringBoot+SpringSecurity+OAuth2.0的环境.这一小节主要对一些写固定InMemory的User和Client进行扩展.实现动 ...