随笔 - 51, 文章 - 1, 评论 - 41, 引用 - 0
数据加载中……

Python写的简易代码统计工具(1)

         用flex写的一个简单代码统计工具》一文中介绍了用flex工具写得C代码统计工具,但功能并不完整,统计子目录不方便。近日在学习python,便有了用python实现代码统计工具的想法。

         整个程序分成两个部分:工作部分和界面部分。工作部分就是执行统计工作。界面部分则负责接受分析用户指令,调用工作部分进行统计和反馈结果。界面部分又分为窗口界面和控制台界面,这些将在(2)中介绍,本文主要介绍工作部分。

         统计工作只是简单区分代码行和注释行,并不对文件进行词法分析,因而较为简单。比如C++代码中,只是识别“/*”与“*/”之间和“//”后面的是注释。而不去分析其他字符是否有意义,符合语法规则。

下面是counter.py的代码:
# -*- coding: cp936 -*-
'''
按照某种语法规则如c,py,统计一个文件或者某个目录下文件中代码和注释的行数
'''
import sys

def LineTypePy(line, info):
    
'''
    根据py的语法规则,分析此行代码属性,使代码还是注释。
    line:此行数据,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):
    
'''
    根据C++的语法规则,分析此行代码属性,使代码还是注释。
    line:此行数据,info附加信息,是否是块注释
    返回值:1代码,2注释,3代码和注释,0空行
    
'''
    state, size 
= 0, len(line)
    line 
= line + '\n' #添加一个字符防止越界
    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== '/':# 块注释结束符
            state |= 2
            info[0] 
= 0
            i 
+= 1
        
else:
            
if info[0] == 0:
                state 
|= 1
            
else:
                state 
|= 2
    
return state

def CounteFile(res, typefunc, filename):
    
'''
    统计文件
    res统计结果,typefunc行属性判断函数,filename文件名
    
'''
    ret 
= [0,0,0,0,0]
    info 
= [0]
    
for line in open(filename, 'rt'):
        ret[typefunc(line, info)] 
+= 1
        ret[
4+= 1 # 代码总行数
    res.append([filename,ret])

def CounteDir(res, typefunc, spath, modes, level):
    
'''
    统计目录下的文件
    res统计结果,typefunc行属性判断函数,spath路径名
    modes文件后缀名,level统计几层子目录,-1为所有子目录
    
'''
    
import os
    
import os.path
    eles 
= os.listdir(spath)
    dirs, files 
= [], []
    
    
#区分文件和目录
    for ele in eles:
        ele 
= os.path.join(spath,ele)
        
if os.path.isdir(ele):
            dirs.append(ele)
        
else:
            files.append(ele)
            
    
# 统计文件
    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)

    
# 判断子目录是否计算完全
    if level == 0:
        
return

    
# 递归计算子目录
    for d in dirs:
        CounteDir(res, typefunc, d, modes, level
-1)


class CodeCounter:
    
'''
    代码统计器的类接口
    
'''
    
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):
        
'''
        统计代码
        result为统计结果
        
'''
        
# 如果统计文件为空,默认统计当前目录
        if self.codefiles == []:
            self.codefiles.append([
'd''.'])
            
        
for ele in self.codefiles:
            
if ele[0] == 'f':   # 统计文件
                CounteFile(result, self.typefunc, ele[1])
            
elif ele[0] == 'd'# 统计目录
                CounteDir(result, self.typefunc, ele[1],
                          self.modes.split(
','), self.level)

    
def SetCodeType(self, codetype):
        
'''
        设置统计代码的类型
        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表示文件类型,f表示文件,d表示目录
        path表示对应的文件或目录名字
        
'''
        
if t == 'f':
            self.codefiles.append([
'f', path])
        
elif t == 'd':
            self.codefiles.append([
'd', path])
        
    
def SetLevel(self, level):
        
'''
        设置统计子目录的层次
        level表示统计几层子目录,0表示只统计当前目录,-1表示所有目录
        
'''
        self.level 
= level

    
def SetModes(self, modes):
        
'''
        设置统计文件的后缀名
        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"
这个程序实现了统计文件和目录的功能。
LineTypePy()函数和LineTypeC()函数分别用py语法和c语法判断该行字符是注释还是代码。
CounteFile()和CounterDir()分别可以统计文件和目录下文件的代码注释。
CodeCounter类则是封装了CounteFile和CounterDir,提供接口。

待续

posted on 2008-01-11 14:45 lemene 阅读(1279) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理