stat.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import chardet
import os
import sys
# 定义注释的格式
# dict的key表示文件的扩展名
# value包含3项,分别是单行注释的开头、多行注释的开头、多行注释的结尾
# 每一项为bytes或包含bytes的tuple
formats = {
'.py': (b'#', (b'\'\'\'', b'"""'), (b'\'\'\'', b'"""')),
'.pyw': (b'#', (b'\'\'\'', b'"""'), (b'\'\'\'', b'"""'))
}
def stat(path):
# 检查输入的路径是一个文件还是一个文件夹
# 如果是文件夹,则遍历其中所有的文件
if os.path.isdir(path):
paths = []
for root, dirs, files in os.walk(path):
paths += [os.path.join(root, f) for f in files]
else:
paths = [path]
fn = 0 # 文件数量
cl = 0 # 代码行数
nl = 0 # 注释行数
el = 0 # 空白行数
tl = 0 # 合计行数
for each in paths:
# 检查文件的扩展名是否在支持的格式中,不在则跳过
try:
single, multi_start, multi_end = formats[os.path.splitext(each)[1]]
fn += 1
except:
continue
# 遍历文件的每一行
# 此处文件以二进制模式打开,忽略编码信息
with open(each, 'rb') as fp:
data = fp.read()
# 如果文件并非纯文本则跳过
if chardet.detect(data)['encoding'] is None:
continue
# 去除BOM
if data.startswith(b'\xef\xbb\xbf'):
data = data[3:]
in_note = False # 标记该行是否在多行注释中
for line in data.splitlines():
line = line.lstrip() # 去除行首的空白
tl += 1
if len(line) == 0:
# 判断是否为空行
el += 1
elif line.startswith(single):
# 判断是否为单行注释
# 此处只检查该行是否只包含注释
# 不统计行尾写有注释的代码行
nl += 1
elif in_note:
# 判断是否包含在多行注释中
nl += 1
if line.endswith(multi_end):
# 判断多行注释是否结束
in_note = False
elif line.startswith(multi_start):
# 判断是否为多行注释的开头
nl += 1
in_note = True
else:
cl += 1
return fn, cl, nl, el, tl
if __name__ == '__main__':
try:
path = sys.argv[1]
except:
path = __file__
fn, cl, nl, el, tl = stat(path)
print('项目名称:{path}\n文件数量:{fn}\n代码行数:{cl}\n注释行数:{nl}\n空白行数:{el}\n合计行数:{tl}'.format(**locals()))