Python 统计项目代码行数

Table of Contents

    统计项目代码行数,下面代码统计的是C/C++语言项目的代码行数,要统计其他语言项目的代码行数也很容易,主要是更改以下地方:

    1. 修改invalid_prefix无效的前缀,valid_suffix有效的后缀
    2. count_code_line_from_text中修改单行注释和多行注释
    3. 如果要排除目录可以在valid_file_list函数里修改

    实现起来其实不难,大概过程是:读取目录里的所有文件,读取单个文件内容,将文件内容用'\n'分隔,然后排除注释和空行就可以了。

    需要注意的是不同文件的编码可能不一样,如果由于默认的文件编码读取失败,可以试着获取文件编码再次用新的编码读取。

    import os
    import chardet
    
    '''
    遍历目录里的所有文件,过滤掉不统计的文件,返回完整的文件路径列表
    'moc_'前缀的文件不统计(Qt自动生成的)
    只统计后缀为'.h', '.cpp', '.c', '.hpp', '.cc'的文件
    '''
    def valid_file_list(dir):
      def invalid_prefix(file):
        for prefix in ['moc_']:
          if file.startswith(prefix):
            return True
        return False
    
      def valid_suffix(file):
        for suffix in ['.h', '.cpp', '.c', '.hpp', '.cc']:
          if file.endswith(suffix):
            return True
        return False
    
      ls = []
      for subdir, dirs, files in os.walk(dir):
        for file in files:
          if invalid_prefix(file):
            continue
          if not valid_suffix(file):
            continue
          filepath = subdir + os.sep + file  
          ls.append(filepath)
      return ls
    
    
    '''
    读取代码文件,默认使用utf-8编码打开。
    如果打开失败,使用chardet获取文件编码,再次使用新的编码打开,
    计算文件是什么编码相对而言是比较耗时的。
    '''
    def read_text(path):
      encoding = 'utf-8'
      try:
        for _ in range(2):
          try:
            with open(path, encoding=encoding) as f:
              return f.read()
          except UnicodeError as e:
            with open(path, 'rb') as f:
              encoding = chardet.detect(f.read())['encoding']
              print('detect encoding:', encoding)
      except Exception as e:
        print(e)
      return ''
    
    
    '''
    统计文本中的代码行数
    排除C++注释,空行
    '''
    def count_code_line_from_text(text):
      ls = str.split(text, '\n')
      ignore = False
      count = 0
      for v in ls:
        v = v.strip()
        if len(v) == 0:
          continue
        if v.startswith('//'):
          continue
        if v.startswith('/*'):
          ignore = True
          # 注意这里不能用continue,否则在同一行会出问题
        if ignore:
          if v.endswith('*/'):
            ignore = False
          continue
        count += 1
      return count
    
    
    '''
    统计目录下所有有效代码文件行数,
    打印文件总数和代码总行数。
    '''
    def count_code_line_from_dir(dir):
      files = valid_file_list(dir)
      total = 0
      for file in files:
        text = read_text(file)
        count = count_code_line_from_text(text)
        print(file, count)
        total += count
      print('total files:%d, lines:%d' % (len(files), total))
    
    
    if __name__ == "__main__":
      count_code_line_from_dir('E:\\project\\test')