|
《用flex寫的一個(gè)簡單代碼統(tǒng)計(jì)工具》一文中介紹了用flex工具寫得C代碼統(tǒng)計(jì)工具,但功能并不完整,統(tǒng)計(jì)子目錄不方便。近日在學(xué)習(xí)python,便有了用python實(shí)現(xiàn)代碼統(tǒng)計(jì)工具的想法。
整個(gè)程序分成兩個(gè)部分:工作部分和界面部分。工作部分就是執(zhí)行統(tǒng)計(jì)工作。界面部分則負(fù)責(zé)接受分析用戶指令,調(diào)用工作部分進(jìn)行統(tǒng)計(jì)和反饋結(jié)果。界面部分又分為窗口界面和控制臺(tái)界面,這些將在(2)中介紹,本文主要介紹工作部分。
統(tǒng)計(jì)工作只是簡單區(qū)分代碼行和注釋行,并不對(duì)文件進(jìn)行詞法分析,因而較為簡單。比如C++代碼中,只是識(shí)別“/*”與“*/”之間和“//”后面的是注釋。而不去分析其他字符是否有意義,符合語法規(guī)則。
下面是counter.py的代碼:
# -*- coding: cp936 -*-
'''
按照某種語法規(guī)則如c,py,統(tǒng)計(jì)一個(gè)文件或者某個(gè)目錄下文件中代碼和注釋的行數(shù)
'''
import sys

def LineTypePy(line, info):
'''
根據(jù)py的語法規(guī)則,分析此行代碼屬性,使代碼還是注釋。
line:此行數(shù)據(jù),info附加信息,在此無意義
返回值:1代碼,2注釋,3代碼和注釋,0空行
'''
state, size = 0, len(line)
line = line + '\n';
i = -1 # 從0開始
while i < size:
i += 1
if line[i] == '\n': # 換行符
break
elif line[i] == ' ' or line[i] == '\t': # 空字符
continue
elif line[i] == '#' or line[i] == ';': # 注釋起始符
state |= 2
else:
state |= 1

return state
def LineTypeC(line, info):
'''
根據(jù)C++的語法規(guī)則,分析此行代碼屬性,使代碼還是注釋。
line:此行數(shù)據(jù),info附加信息,是否是塊注釋
返回值:1代碼,2注釋,3代碼和注釋,0空行
'''
state, size = 0, len(line)
line = line + '\n' #添加一個(gè)字符防止越界
i = -1
while i < size:
i += 1
if line[i] == '\n': # 換行符
break
elif line[i] == ' ' or line[i] == '\t': # 空字符
continue
elif line[i] == '/' and line[i+1] == '/':# 行注釋
state |= 2
i += 1
elif line[i] == '/' and line[i+1] == '*':# 塊注釋開始符
state |= 2
info[0] = 1
i += 1
elif line[i] == '*' and line[i+1] == '/':# 塊注釋結(jié)束符
state |= 2
info[0] = 0
i += 1
else:
if info[0] == 0:
state |= 1
else:
state |= 2
return state

def CounteFile(res, typefunc, filename):
'''
統(tǒng)計(jì)文件
res統(tǒng)計(jì)結(jié)果,typefunc行屬性判斷函數(shù),filename文件名
'''
ret = [0,0,0,0,0]
info = [0]
for line in open(filename, 'rt'):
ret[typefunc(line, info)] += 1
ret[4] += 1 # 代碼總行數(shù)
res.append([filename,ret])

def CounteDir(res, typefunc, spath, modes, level):
'''
統(tǒng)計(jì)目錄下的文件
res統(tǒng)計(jì)結(jié)果,typefunc行屬性判斷函數(shù),spath路徑名
modes文件后綴名,level統(tǒng)計(jì)幾層子目錄,-1為所有子目錄
'''
import os
import os.path
eles = os.listdir(spath)
dirs, files = [], []
#區(qū)分文件和目錄
for ele in eles:
ele = os.path.join(spath,ele)
if os.path.isdir(ele):
dirs.append(ele)
else:
files.append(ele)
# 統(tǒng)計(jì)文件
for f in files:
isokfile = True
if modes == []:
pass
else:
for m in modes:
if f[-len(m):] == m:
break
else:
isokfile = False
if isokfile:
CounteFile(res, typefunc, f)

# 判斷子目錄是否計(jì)算完全
if level == 0:
return

# 遞歸計(jì)算子目錄
for d in dirs:
CounteDir(res, typefunc, d, modes, level-1)


class CodeCounter:
'''
代碼統(tǒng)計(jì)器的類接口
'''
def __init__(self,codefiles=[],modes='.c,.h,.cpp',typefunc=LineTypeC,
codetype='c',level=1):
self.level = level
self.modes = modes
self.codefiles = codefiles
self.typefunc = typefunc
self.codetype = codetype

def Count(self, result):
'''
統(tǒng)計(jì)代碼
result為統(tǒng)計(jì)結(jié)果
'''
# 如果統(tǒng)計(jì)文件為空,默認(rèn)統(tǒng)計(jì)當(dāng)前目錄
if self.codefiles == []:
self.codefiles.append(['d', '.'])
for ele in self.codefiles:
if ele[0] == 'f': # 統(tǒng)計(jì)文件
CounteFile(result, self.typefunc, ele[1])
elif ele[0] == 'd': # 統(tǒng)計(jì)目錄
CounteDir(result, self.typefunc, ele[1],
self.modes.split(','), self.level)

def SetCodeType(self, codetype):
'''
設(shè)置統(tǒng)計(jì)代碼的類型
codetype: py表示Python語言,c表示c或c++
'''
if codetype == 'py':
self.typefunc = LineTypePy
else:
self.typefunc = LineTypeC
self.codetype = codetype

def AddCodeFiles(self, t, path):
'''
增加統(tǒng)計(jì)文件
t表示文件類型,f表示文件,d表示目錄
path表示對(duì)應(yīng)的文件或目錄名字
'''
if t == 'f':
self.codefiles.append(['f', path])
elif t == 'd':
self.codefiles.append(['d', path])
def SetLevel(self, level):
'''
設(shè)置統(tǒng)計(jì)子目錄的層次
level表示統(tǒng)計(jì)幾層子目錄,0表示只統(tǒng)計(jì)當(dāng)前目錄,-1表示所有目錄
'''
self.level = level

def SetModes(self, modes):
'''
設(shè)置統(tǒng)計(jì)文件的后綴名
modes 后綴名列表。例如[.c,.h]
'''
self.modes = modes
if __name__ == '__main__':
res = []
counter = CodeCounter()
counter.Count(res)

stat = [0,0,0,0,0]
for ele in res:
print ele[1][4], ele[1][1]+ele[1][3], ele[1][2]+ele[1][3], ele[1][0], ele[0]
for i in range(0, len(stat)):
stat[i] += ele[1][i]

print stat[4], stat[1]+stat[3], stat[2]+stat[3],stat[0],"Total"
這個(gè)程序?qū)崿F(xiàn)了統(tǒng)計(jì)文件和目錄的功能。 LineTypePy()函數(shù)和LineTypeC()函數(shù)分別用py語法和c語法判斷該行字符是注釋還是代碼。 CounteFile()和CounterDir()分別可以統(tǒng)計(jì)文件和目錄下文件的代碼注釋。 CodeCounter類則是封裝了CounteFile和CounterDir,提供接口。 待續(xù)
|