代码行统计工具(C#)

开发环境介绍:
VS2013下的Windows窗体应用程序,程序运行时的初始界面如下:

操作步骤:
(1)选择操作系统下的某个文件目录,可以是文件夹目录,也可以是文件目录
(2)几点约束:
是否统计子文件夹里的文件的代码行数;
给定想要统计的代码文件的扩展名,即后缀名;
自定义排除统计某些行(匹配正则表达式);
(3)点击“开始统计”
(4)将统计结果(展示在DataGridView中)导出到EXCEL中
知识点深究:
(1)类的构造
首先展示本例中的一个类

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Text.RegularExpressions;

namespace CodeLineStatistics.Lib
{
    /// <summary>
    ///封装文件的代码行数的统计信息的类
    /// </summary>

    public  class CodeFileCounter
    {
       //私有字段
        private string filePath;
        private string customExcludePattern;
        private bool notContainEmpty;
        private int emptyLineCount=0;
        private int customExcludeLineCount=0;
        private int singleCommentLineCount=0;
        private int multiCommentLineCount=0;
        private int codeLineCount=0;
        private int totalCount=0;
        private string[] lines;

        private bool  isCalculated = false;//默认为false

        //构造函数(带参)
        /// <summary>
        /// 构造一个统计代码文件的对象(其参数应该是应用程序接口传入的值,如文件夹路径,自定义排除模式,不统计空行)
        /// </summary>
        /// <param name="filePath">单个文件的路径</param>
        /// <param name="customExcludePattern">自定义排除模式</param>
        /// <param name="notContainEmpty">不统计空行</param>
        public CodeFileCounter(string codeFilePath,string customExclusions,bool EmptyNotContained)
        {
            this.filePath = codeFilePath;
            this.customExcludePattern=customExclusions.Replace("\r\n", "|").Replace("\n", "|");
            this.notContainEmpty = EmptyNotContained;         
        }

        //只读属性
        public String FilePath //文件的虚拟路径
        {
            get
            {
                return filePath;
            }
        }
        public String CustomExcludePattern //自定义排除模式
        {
            get
            {
                return customExcludePattern;
            }
        }
        public Boolean NotContainEmpty //是否不包含空行
        {
            get
            {
                return notContainEmpty;
            }

        }
        public Int32 EmptyLineCount//空白行
        {
            get 
            {
                EnsureCalculate();
                return emptyLineCount;
            }
        }
        public Int32 CustomExcludeLineCount//自定义排除行,象引用行
        {
            get
            {
                EnsureCalculate();
                return customExcludeLineCount;
            }
        }
        public Int32 SingleCommentLineCount //单行注释行
        {
            get
            {
                EnsureCalculate();
                return singleCommentLineCount;
            }
        }
        public Int32 MultiCommentLineCount //多行注释行
        {
            get
            {
                EnsureCalculate();
                return multiCommentLineCount;
            }
        }
        public Int32 CodeLineCount//代码行
        {
            get
            {
                return TotalCount - EmptyLineCount - SingleCommentLineCount - MultiCommentLineCount - CustomExcludeLineCount;
            }
        }
        public Int32 TotalCount //总行数
        {
            get
            {
                return Lines.Length;
            }
        }
        public string[] Lines
        {
            get
            {
                if (lines == null)
                {
                    lines = GetFileLines(FilePath);
                }
                return lines;
            }
        }
        //匹配单行注释 //.*
        //匹配多行注释 /\*[\w\W]*?\*/

// 说明:
// /\*: 注释开始时的 /*
// [\w\W]*?: 用非贪婪模式匹配任意长度任意字符。(非贪婪模式指:尽可能少的去匹配,这时,后面的匹配结束部分的标识会尽可能早的起作用。)
// \*/:匹配注释结束的*/

        public void EnsureCalculate()
        {
            if (!isCalculated)//若没有计算过该行
            {
                Count();
                isCalculated = true;
            }
        }
        /// <summary>
        ///统计每个文件中的空白行、单行注释行、多行注释行、自定义排除行
        /// </summary>
        public void Count()
        { 
           Regex EmptyLineReg=new Regex(@"^\s*$");//"\s"匹配任何空白字符,包括空白符、制表符、换页符等,“*”匹配前面的子表达式零次或者多次
           Regex SingleCommentReg = new Regex(@"^//.*$");
           Regex MultiCommentReg = new Regex(@"^/\*[\w\W]*?\*/$");
           Regex CustomExcludeReg = new Regex(CustomExcludePattern);

           this.emptyLineCount = 0;
           this.singleCommentLineCount = 0;
           this.multiCommentLineCount = 0;
           this.customExcludeLineCount = 0;
           foreach (var line in Lines)
           {
               if (NotContainEmpty && EmptyLineReg.IsMatch(line))//排除模式中“不包含空行”即要统计空行
               {
                   emptyLineCount++;
               }
               else if (SingleCommentReg.IsMatch(line))
               {
                   singleCommentLineCount++;
               }
               else if (MultiCommentReg.IsMatch(line))
               {
                   multiCommentLineCount++;
               }
               else if (CustomExcludePattern.Length > 0 && CustomExcludeReg.IsMatch(line))
               {
                   customExcludeLineCount++;
               }
               else //代码行
               {
                   //CodeLineCount++;
               }
           }      
        }
        /// <summary>
        /// 获取一个指定路径的文件里的所有行
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <returns>字符串行数组</returns>
        public  string[] GetFileLines(string filePath)
        {
            using (FileStream fs=new FileStream(filePath,FileMode.Open,FileAccess.Read))//使用指定路径、创建模式和读写权限初始化实例(创建文件流)
            {
                StreamReader sr = new StreamReader(fs, Encoding.Default);//用指定的编码为指定的流创建从字节中读取字符的对象
                string[] lines=sr.ReadToEnd().Split(new string[] { "\r\n", "\n" }, StringSplitOptions.None);//返回空数组元素
                return lines;
            }
        }

    }
}

说明:
CodeFileCounter这个类的主要功能是:针对某个代码文件(文件路径,注意这里并非文件夹路径)的统计信息(空白行、单行注释行、多行注释行、自定义排除行、代码行、代码总行数)。
该类由三部分组成:字段、属性、方法。首先说字段,在C#中,我们可以非常自由的、毫无限制的访问公有字段,但在一些场合中,我们可能希望限制只能给字段赋于某个范围的值、或是要求字段只能读或只能写,或是在改变字段时能改变对象的其他一些状态,这些单靠字段是无法做到的。当然也有私有字段;然后说说属性,属性是类、结构或接口的命名成员,属性将类中的字段公开,通过属性的set和get访问器访问字段,它提供了一种抽象级别来允许您更改字段。set块负责属性的写入工作,get块负责属性的读取工作。在两个块中都可以做一些其他操作,如在set中验证赋的值是否符合要求并决定是否进行赋值。当缺少其中一块时属性就只能读或只能写,set和get块中属性必需有一个,因为即不能读又不能写的属性是没有意义的。
属性的组成: 私有字段(之所以是私有的是因为只能通过属性来赋值)、get访问器(读取数据,return字段值或者用于计算并返回字段值,还可以throw终止,可包含简单验证逻辑)、set访问器(给属性赋值,类似于一个返回void的方法)
属性的分类:按访问修饰符来分:public/private/protected/internal,定义类的用户如何才能访问属性;
(static)静态属性,实例属性,(virtual)虚属性,抽(abstract)象属性;
只读、只写、可读写属性;
属性和字段的区别
相同点:
都是类的成员,属性是类的属性,而字段是类的数据成员
不同点:
1 属性可进行数据绑定
2 属性可通过set和get方法进行数据安全性检验,而字段不行
3 属性可进行线程同步
public string Name
{
set{
lock(this)
{
}
}
}
4 属性可以是抽象的,而字段不行
5 属性可以接口的形式表现
6 基于属性的索引
7 不要直接把字段转化为属性
8 字段在值的处理上并不是那么的灵活,给它赋什么它就是什么,不允许经过逻辑处理。如果把一个人的身高写成一个字段,给它赋值1000M,这显示是不正常的数据,字段无法处理这种特殊数据。
9 与字段不同,属性不作为变量来分类。因此,不能将属性作为 ref参数或 out参数传递。
10 通常属性的名称和它所访问的内部成员的名称相同,只不过首字母大写。且set服务器中使用的隐式参数value(该参数具有基础成员变量的类型);
最后说说方法,一个是构造函数,另一个是普通方法。若没有显示的构造函数,则系统会默认一个无参的构造函数(没有返回类型),这是有参构造函数的定义:public CodeFileCounter(string codeFilePath,string customExclusions,bool EmptyNotContained){},其中的形参是由应用程序提供实参,另一些方法规定类的操作。
(2)获取所有文件的统计信息

List<CodeFileCounter> codeFileCounterList = GetAllFilesStatistics(txtFilePath.Text, chkBoxIsContained.Checked, chkEmptyLine.Checked, txtSourceFileExtension.Text, txtExcludePattern.Text);

(3)将统计信息填充到DataGridView

  FillToGrid(codeFileCounterList, txtFilePath.Text);

(4)将DataGridView里的数据导出到Excel中

  private void btnExport_Click(object sender, EventArgs e)
     {
            SaveToExcel(gvShowDetail);
     }

你可能感兴趣的:(代码行统计工具(C#))