使用Python设计一个代码统计工具


Posted in Python onApril 04, 2018

问题

设计一个程序,用于统计一个项目中的代码行数,包括文件个数,代码行数,注释行数,空行行数。尽量设计灵活一点可以通过输入不同参数来统计不同语言的项目,例如:

# type用于指定文件类型
python counter.py --type python

输出:

files:10
code_lines:200
comments:100
blanks:20

分析

这是一个看起来很简单,但做起来有点复杂的设计题,我们可以把问题化小,只要能正确统计一个文件的代码行数,那么统计一个目录也不成问题,其中最复杂的就是关于多行注释,以 Python 为例,注释代码行有如下几种情况:

1、井号开头的单行注释

# 单行注释

2、多行注释符在同一行的情况

"""这是多行注释"""
'''这也是多行注释'''
3、多行注释符

"""
这3行都是注释符
"""

我们的思路采取逐行解析的方式,多行注释需要一个额外的标识符in_multi_comment 来标识当前行是不是处于多行注释符当中,默认为 False,多行注释开始时,置为 True,遇到下一个多行注释符时置为 False。从多行注释开始符号直到下一个结束符号之间的代码都应该属于注释行。

知识点

如何正确读取文件,读出的文件当字符串处理时,字符串的常用方法

简化版

我们逐步进行迭代,先实现一个简化版程序,只统计Python代码的单文件,而且不考虑多行注释的情况,这是任何入门 Python 的人都能实现的功能。关键地方是把每一行读出来之后,先用 strip() 方法把字符串两边的空格、回车去掉

# -*- coding: utf-8 -*-
"""
只能统计单行注释的py文件
"""
def parse(path):
 comments = 0
 blanks = 0
 codes = 0
 with open(path, encoding='utf-8') as f:
 for line in f.readlines():
  line = line.strip()
  if line == "":
  blanks += 1
  elif line.startswith("#"):
  comments += 1
  else:
  codes += 1
 return {"comments": comments, "blanks": blanks, "codes": codes}
if __name__ == '__main__':
 print(parse("xxx.py"))

多行注释版

如果只能统计单行注释的代码,意义并不大,要解决多行注释的统计才能算是一个真正的代码统计器

# -*- coding: utf-8 -*-
"""

可以统计包含有多行注释的py文件

"""
def parse(path):
 in_multi_comment = False # 多行注释符标识符号
 comments = 0
 blanks = 0
 codes = 0
 with open(path, encoding="utf-8") as f:
 for line in f.readlines():
  line = line.strip()
  # 多行注释中的空行当做注释处理
  if line == "" and not in_multi_comment:
  blanks += 1
  # 注释有4种
  # 1. # 井号开头的单行注释
  # 2. 多行注释符在同一行的情况
  # 3. 多行注释符之间的行
  elif line.startswith("#") or \
    (line.startswith('"""') and line.endswith('"""') and len(line)) > 3 or \
   (line.startswith("'''") and line.endswith("'''") and len(line) > 3) or \
   (in_multi_comment and not (line.startswith('"""') or line.startswith("'''"))):
  comments += 1
  # 4. 多行注释符的开始行和结束行
  elif line.startswith('"""') or line.startswith("'''"):
  in_multi_comment = not in_multi_comment
  comments += 1
  else:
  codes += 1
 return {"comments": comments, "blanks": blanks, "codes": codes}
if __name__ == '__main__':
 print(parse("xxx.py"))

上面的第4种情况,遇到多行注释符号时,in_multi_comment 标识符进行取反操作是关键操作,而不是单纯地置为 False 或 True,第一次遇到 """ 时为True,第二次遇到 """ 就是多行注释的结束符,取反为False,以此类推,第三次又是开始,取反又是True。

那么判断其它语言是不是要重新写一个解析函数呢?如果你仔细观察的话,多行注释的4种情况可以抽象出4个判断条件,因为大部分语言都有单行注释,多行注释,只是他们的符号不一样而已。

CONF = {"py": {"start_comment": ['"""', "'''"], "end_comment": ['"""', "'''"], "single": "#"},
 "java": {"start_comment": ["/*"], "end_comment": ["*/"], "single": "//"}}
start_comment = CONF.get(exstansion).get("start_comment")
end_comment = CONF.get(exstansion).get("end_comment")
cond2 = False
cond3 = False
cond4 = False
for index, item in enumerate(start_comment):
 cond2 = line.startswith(item) and line.endswith(end_comment[index]) and len(line) > len(item)
 if cond2:
 break
for item in end_comment:
 if line.startswith(item):
 cond3 = True
 break
for item in start_comment+end_comment:
 if line.startswith(item):
 cond4 = True
 break
if line == "" and not in_multi_comment:
 blanks += 1
# 注释有4种
# 1. # 井号开头的单行注释
# 2. 多行注释符在同一行的情况
# 3. 多行注释符之间的行
elif line.startswith(CONF.get(exstansion).get("single")) or cond2 or \
 (in_multi_comment and not cond3):
 comments += 1
# 4. 多行注释符分布在多行时,开始行和结束行
elif cond4:
 in_multi_comment = not in_multi_comment
 comments += 1
else:
 codes += 1

只需要一个配置常量把所有语言的单行、多行注释的符号标记出来,对应出 cond1到cond4几种情况就ok。剩下的任务就是解析多个文件,可以用 os.walk 方法。

def counter(path):
 """
 可以统计目录或者某个文件
 :param path:
 :return:
 """
 if os.path.isdir(path):
 comments, blanks, codes = 0, 0, 0
 list_dirs = os.walk(path)
 for root, dirs, files in list_dirs:
  for f in files:
  file_path = os.path.join(root, f)
  stats = parse(file_path)
  comments += stats.get("comments")
  blanks += stats.get("blanks")
  codes += stats.get("codes")
 return {"comments": comments, "blanks": blanks, "codes": codes}
 else:
 return parse(path)

当然,想要把这个程序做完善,还有很多工作要多,包括命令行解析,根据指定参数只解析某一种语言。

补充:

Python实现代码行数统计工具

我们经常想要统计项目的代码行数,但是如果想统计功能比较完善可能就不是那么简单了, 今天我们来看一下如何用python来实现一个代码行统计工具。

思路:

首先获取所有文件,然后统计每个文件中代码的行数,最后将行数相加.

实现的功能:

统计每个文件的行数;
统计总行数;
统计运行时间;
支持指定统计文件类型,排除不想统计的文件类型;
递归统计文件夹下包括子文件件下的文件的行数;

排除空行;

# coding=utf-8
import os
import time
basedir = '/root/script'
filelists = []
# 指定想要统计的文件类型
whitelist = ['php', 'py']
#遍历文件, 递归遍历文件夹中的所有
def getFile(basedir):
 global filelists
 for parent,dirnames,filenames in os.walk(basedir):
  #for dirname in dirnames:
  # getFile(os.path.join(parent,dirname)) #递归
  for filename in filenames:
   ext = filename.split('.')[-1]
   #只统计指定的文件类型,略过一些log和cache文件
   if ext in whitelist:
    filelists.append(os.path.join(parent,filename))
#统计一个文件的行数
def countLine(fname):
 count = 0
 for file_line in open(fname).xreadlines():
  if file_line != '' and file_line != '\n': #过滤掉空行
   count += 1
 print fname + '----' , count
 return count
if __name__ == '__main__' :
 startTime = time.clock()
 getFile(basedir)
 totalline = 0
 for filelist in filelists:
  totalline = totalline + countLine(filelist)
 print 'total lines:',totalline
 print 'Done! Cost Time: %0.2f second' % (time.clock() - startTime)

结果:

[root@pythontab script]# python countCodeLine.py
/root/script/test/gametest.php---- 16
/root/script/smtp.php---- 284
/root/script/gametest.php---- 16
/root/script/countCodeLine.py---- 33
/root/script/sendmail.php---- 17
/root/script/test/gametest.php---- 16
total lines: 382
Done! Cost Time: 0.00 second
[root@pythontab script]#

只会统计php和python文件,非常方便。

总结

以上所述是小编给大家介绍的使用Python设计一个代码统计工具,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Python 相关文章推荐
python文件比较示例分享
Jan 10 Python
Django1.7+python 2.78+pycharm配置mysql数据库教程
Nov 18 Python
Python列表推导式与生成器表达式用法示例
Feb 08 Python
Python 调用 zabbix api的方法示例
Jan 06 Python
20行python代码的入门级小游戏的详解
May 05 Python
python 的 openpyxl模块 读取 Excel文件的方法
Sep 09 Python
python 两个数据库postgresql对比
Oct 21 Python
python中安装django模块的方法
Mar 12 Python
Keras Convolution1D与Convolution2D区别说明
May 22 Python
浅谈Python中的生成器和迭代器
Jun 19 Python
python IP地址转整数
Nov 20 Python
PYTHON InceptionV3模型的复现详解
May 06 Python
用 Python 连接 MySQL 的几种方式详解
Apr 04 #Python
Python基于辗转相除法求解最大公约数的方法示例
Apr 04 #Python
对numpy中数组元素的统一赋值实例
Apr 04 #Python
Python 元类实例解析
Apr 04 #Python
对numpy 数组和矩阵的乘法的进一步理解
Apr 04 #Python
Numpy数组的保存与读取方法
Apr 04 #Python
基于Python Numpy的数组array和矩阵matrix详解
Apr 04 #Python
You might like
PHP json_decode函数详细解析
2014/02/17 PHP
php获取网站百度快照日期的方法
2015/07/29 PHP
PHP判断上传文件类型的解决办法
2015/10/20 PHP
PHP通过引用传递参数用法分析
2016/12/01 PHP
[企业公众号]升级到[企业微信]之后发送消息失败的解决方法
2017/06/30 PHP
Event altKey,ctrlKey,shiftKey属性解析
2013/12/18 Javascript
javascript得到当前页的来路即前一页地址的方法
2014/02/18 Javascript
js实现简单锁屏功能实例
2015/05/27 Javascript
js实现不重复导入的方法
2016/03/02 Javascript
MVC+jQuery.Ajax异步实现增删改查和分页
2020/12/22 Javascript
jquery树形菜单效果的简单实例
2016/06/06 Javascript
jQuery使用animate实现ul列表项相互飘动效果示例
2016/09/16 Javascript
php register_shutdown_function函数详解
2017/07/23 Javascript
浅谈node中的exports与module.exports的关系
2017/08/01 Javascript
简单实现js进度条加载效果
2020/03/25 Javascript
使用angular-cli webpack创建多个包的方法
2018/10/16 Javascript
基于Vue实现平滑过渡的拖拽排序功能
2019/06/12 Javascript
Python解释执行原理分析
2014/08/22 Python
python定时利用QQ邮件发送天气预报的实例
2017/11/17 Python
Python实现爬取百度贴吧帖子所有楼层图片的爬虫示例
2018/04/26 Python
Python把对应格式的csv文件转换成字典类型存储脚本的方法
2019/02/12 Python
Python 的AES加密与解密实现
2019/07/09 Python
解决Django layui {{}}冲突的问题
2019/08/29 Python
Python基于yield遍历多个可迭代对象
2020/03/12 Python
tensorflow dataset.shuffle、dataset.batch、dataset.repeat顺序区别详解
2020/06/03 Python
python如何停止递归
2020/09/09 Python
python 解决函数返回return的问题
2020/12/05 Python
HTML5 Video/Audio播放本地文件示例介绍
2013/11/18 HTML / CSS
PacSun官网:加州生活方式服装、鞋子和配饰
2018/03/10 全球购物
2014年党务公开实施方案
2014/02/27 职场文书
小学“向国旗敬礼”网上签名寄语活动总结
2014/09/27 职场文书
十七岁的单车观后感
2015/06/12 职场文书
汤姆索亚历险记读书笔记
2015/06/29 职场文书
少儿励志名言(80句)
2019/08/14 职场文书
Python matplotlib安装以及实现简单曲线的绘制
2022/04/26 Python
CSS 实现角标效果的完整代码
2022/06/28 HTML / CSS