问题来源,在14年的暑假的一次小项目当中遇到了一个这样的问题,要求统计C++代码的注释行数,有效代码行数,代码注释公共行数,以及函数个数。

下面稍微解释一下问题,

1)注释行数:指有注释的行,包括有代码和注释的公共行(如:3,4,15,22...)

2)有效代码行:指有代码的行,包括有代码和注释的公共行(如:1,4,11,15,25....)

3)代码注释公共行:指又有代码又有注释的行(如:4,15...)

4)函数个数:这个不用说明了吧。

以下为注释情况展示代码:

 #include <stdio.h>

 //follow is a common line
void swap(char *p/* = NULL */)
{
printf("this is a function /*is not comments*/");
} int main(void)
{
int a = ;
int b;
char *p = NULL; swap(p); //common line
if ( == a)
{
printf("is not function;//is not comments");
}
//pure comments/*no use*/ /*a = 5;
printf("not use\n");*/ b = ;/*i'm a comment*/ a = ; /*comments line
pure //comments line;"*/......"look there*/
printf("test \" escape char");
//and the follow,maybe affect the count of function
if (*p == '{')
{
printf("the '{' is a question\n");
}
// return ;
}

上面的这个代码片段,已经基本上展示了各种注释可能出现的情况,接着我们来分析一下注释出现的位置:

一、“//”双斜杠注释

1)可能出现在行头部,如:第3行;

2)可能出现在行末尾,如:第15行;

注意:第18行,第26行。第18行中,//位于双引号中,注释失效;第26行中,//位于/**/注释中,//失效。

二、“/**/”斜杠星注释

1)可能出现在函数参数里,如:第4行;

2)可能注释一个段落,如:22,23行;

3)也可能出现在代码中部,如25,26行那样的复杂注释;

注意:第20行,第26行。第20行,/**/位于//注释中,失效。第26行,有一个*/位于“”中,*/失效。

好了,位置分析完毕,下面分析一下如何设计算法:

三、具体算法设计思路

1)解决文件读取

这里我们用c++文件读取,每次读取一行,将读取结果放到string变量里面,这样string变量尾部自动为'\0'。我们用于判断行尾部。

2)双引号问题

由于双引号里面的内容都是无效的,所以我们可以直接来个过滤双引号里面的内容。(具体实现见代码)

3)单引号问题

上面的展示代码里面也看到了,单引号里面的内容(如:if (ch == '{'))可能会影响函数个数的统计,所以我们需要过滤单引号。

4)空行

这个最简单,如果string为空就是空行。包括(\t\n\r' '等等,无效字符,都算空行)。

5)“//”注释

这个较为简单,遇到//就可以进行统计,同时该行也不需要继续遍历了,直接break;然后读取下一行。

6)“/**/”注释

较麻烦,我们这里用到了代码标记,注释标记,同时还设置了当遍历到行最尾部的时候,才进行行数统计,这样避免了一行被统计多次。(具体实现见代码)

7)函数个数

首先说一下统计思路,我们统计函数的时候,只是以大括号({})为统计标记。用栈来表示,遇到左括号,入栈;遇到右括号,弹出一个左括号。知道弹到栈空,函数个数+1,

这样的话,就实现了只保留最外层那对{},里层的括号全部抵销。我们又想了一下,简化了一下,不用栈,直接用一个变量来表示括号个数,遇到左括号,++top;遇到右括号--top,直到top==0,也就相当于栈空,函数个数+1。

其实如下代码的函数个数统计还有问题:

示例:对于类、结构体、枚举体、全局数组,会被统计为一个函数。也就是说,那些在函数体外面的,但是又带有大括号({})的代码,都会被识别为函数。

以下为实现代码:

 #include <iostream>
#include <fstream>
#include <string>
using namespace std; int main(void)
{
string line="";
ifstream ifs;
ifs.open("Test.cpp");
if (!ifs)
{
cout<<"文件打开失败"<<endl;
exit();
}
//////////////////////////////////////////////
//标记:双引号 斜杠星 函数 代码 注释
int bSyh = , bXgx = , bHs = -, bCode = , bZs = ;
// "" '' // /* {}
/////////////////////////////////////////////
//个数:空行 注释 代码 公共 函数
int i,nKh = , nZs = , nDm = , nGg = , nHs = ;
//
while (!ifs.eof())
{
i = ;
getline(ifs,line); //读取一行文件
bCode = ; //该行没有代码
bZs = ; //该行没有注释
if (bXgx) //bXgx 斜杠星注释标记
bZs = ; //该行有注释
//过滤无效符号
while (line[i] == ' ' || line[i] == '\t' || line[i] == '\r' || line[i] == '\n')
{
++i;
}
//“以下为空行统计区域:开始”
if (!bXgx && line[i] == '\0') //空行
{
++nKh;
continue;
}
//“空行统计:结束”
while ()
{
//第一次遇到双引号 引号为非转义字符(\")
if (!bSyh && line[i] == '\"' && ((i > && line[i-] != '\\') || (i == )))
{
++i;
bSyh = ;
continue;
}
//“正在进行双引号屏蔽....”
if (bSyh)
{
//“ \”结束”
if (line[i] == '\"' && ((i > && line[i-] != '\\') || (i == )))
{
bSyh = ;
}
else if (line[i] == '\0') //行末尾
{
if (bZs)
++nZs;
if (bCode)
++nDm;
if (bZs && bCode)
++nGg;
break;
}
++i;
continue;
}
//遇到单引号(避免'{','}'),且非转义字符\',连续跳过3个(第二个'后位置)
if (line[i] == '\'' && ((i > && line[i-] != '\\') || (i == )))
{
i += ;
continue;
}
//“//注释行”
if (!bXgx && line[i] == '/' && line[i+] == '/')
{
if (bCode) //“前有代码,混合注释行”
{
++nZs; //注释
++nDm; //代码
++nGg; //公共
}
else //纯注释行
{
++nZs;
}
break; //跳出当前行(即,内while循环),“//”后代码不做判断
}
//“/*注释开始”
if (!bXgx && line[i] == '/' && line[i+] == '*')
{
i += ; //跳过/*符号
bXgx = ; //标记“/*”开始
bZs = ; //“发现注释”
continue;
}
//“正在进行多行注释....”
if (bXgx)
{
//“*/注释结束”
if (line[i] == '*' && line[i+] == '/')
{
++i; //“跳过*/”注意后有一个 ++i;
bXgx = ;
}
else if (line[i] == '\0') //行末尾
{
if (bCode) //注释前有代码,即“混合行”
{
++nDm;
++nZs;
++nGg;
}
else
{
++nZs; //“纯注释”
}
break;
}
++i;
continue;
}
if (line[i] == '\0')
{
if (bZs)
++nZs;
if (bCode)
++nDm;
if (bZs && bCode)
++nGg;
break;
}
//“以下全是有效代码区域”
//“函数个数统计区域:开始”
if (line[i] == '{') //记录函数左括号
{
++bHs;
}
else if (line[i] == '}') //遇到函数右括号
{
if (bHs == ) //“发现一个函数”
++nHs;
--bHs;
}
//“函数统计:结束”
++i;
bCode = ; //能执行到这里,说明该行存在代码
}
} cout<<"注释: "<<nZs<<endl;
cout<<"代码: "<<nDm<<endl;
cout<<"空行: "<<nKh<<endl;
cout<<"公共: "<<nGg<<endl;
cout<<"函数: "<<nHs<<endl; return ;
}

原文地址:http://www.cnblogs.com/nchar/p/3915889.html

C++统计代码注释行数 & 有效代码行数 & 代码注释公共行 & 函数个数的更多相关文章

  1. 记-统计svn与git的log日志中的代码行变更

    任务要求 统计指定时间内,指定git地址与svn地址上的所有人员的代码行变更情况. 解决方案 最初为根据数据库中存储的所有git与svn地址来统计所有人员的提交代码行.之后由于库中存储的地址不全,改为 ...

  2. 如何用Python统计《论语》中每个字的出现次数?10行代码搞定--用计算机学国学

    编者按: 上学时听过山师王志民先生一场讲座,说每个人不论干什么,都应该学习国学(原谅我学了计算机专业)!王先生讲得很是吸引我这个工科男,可能比我的后来的那些同学听课还要认真些,当然一方面是兴趣.一方面 ...

  3. NotePad ++的妙用:添加代码行数和格式不变复制代码

    NotePad ++ 不仅安装包小而且功能强大,可以支持很多语言.这里简单阐述下两个功能: 一.在代码前添加行数: 1.用NotePad ++打开一个文件,一般NotePad ++会自动识别这是什么语 ...

  4. 不到50行代码实现一个能对请求并发数做限制的通用RequestDecorator

    使用场景 在开发中,我们可能会遇到一些对异步请求数做并发量限制的场景,比如说微信小程序的request并发最多为5个,又或者我们需要做一些批量处理的工作,可是我们又不想同时对服务器发出太多请求(可能会 ...

  5. 多行滚动jQuery循环新闻列表代码

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  6. 代码的坏味道(13)——过多的注释(Comments)

    坏味道--过多的注释(Comments) 特征 注释本身并不是坏事.但是常常有这样的情况:一段代码中出现长长的注释,而它之所以存在,是因为代码很糟糕. 问题原因 注释的作者意识到自己的代码不直观或不明 ...

  7. 帮初学者改代码——playerc之“练习:求完数问题”(下)

    前文链接:帮初学者改代码——playerc之“练习:求完数问题”(上) 再来看看be_ferfect()应该如何改. be_ferfect()函数的功能是判断number是否为完数,同时把因子对写入d ...

  8. 帮初学者改代码——playerc之“练习:求完数问题”(上)

    原文:“练习:求完数问题” 原代码: // #include <stdio.h> #include <stdlib.h> #include <math.h> #de ...

  9. Python 1行代码实现文本分类(实战笔记),含代码详细说明及运行结果

    Python 1行代码实现文本分类(实战笔记),含代码详细说明及运行结果 一.详细说明及代码 tc.py =============================================== ...

随机推荐

  1. 2. Singleton模式

    这两种方法都提到了使用模版: (1) 参考文章:http://www.cnblogs.com/08shiyan/archive/2012/03/16/2399617.html 不同: a. 该方法直接 ...

  2. Matlab:拟合(2)

    非线性最小二乘拟合: 解法一:用命令lsqcurvefit function f = curvefun(x, tdata) f = x() + x()*exp() * tdata); %其中x() = ...

  3. jQuery Ajax 实例 ($.ajax、$.post、$.get)【转载】

    本文转载自:http://jun1986.iteye.com/blog/1399242 Jquery在异步提交方面封装的很好,直接用AJAX非常麻烦,Jquery大大简化了我们的操作,不用考虑浏览器的 ...

  4. Java学习日记-2 零零碎碎

    一.使用Scanner类进行控制台的输入 文档中Scanner类的定义为A simple text scanner which can parse primitive types and string ...

  5. java并发ThreadLocal

    ThreadLocal 实际就是一个map,一个线程对应一个local对象,线程创建时候,threadlocal随着创建,线程死亡ThreadLocal对象随着消失. runnable可以共享数据,要 ...

  6. [oracle安装错误处理]ORA-00600: [keltnfy-ldmInit][46], [1], []

    原博文:http://blog.itpub.net/519536/viewspace-614893/ 今天在部署一套10g Oracle(10.2.0.3版本)的过程中,偶遇ORA-00600: in ...

  7. 部分实用的SQL语句

    一.在数据库创建表格的SQL语句 1,创建一个link表格,包含属性:lid  主键,title 标题,  imgpath 图片地址 , url  网址  , info 说明,  isshow 显示1 ...

  8. Android Toast封装

    package com.whoop.mobile.trace.util; import android.content.Context; import android.content.res.Reso ...

  9. BitBlt介绍

    设备上下文画图有非常多种方法.比如通过创建位图画刷,利用其填充一个区域来实现图像的绘制.此外,还能够使用CDC类的位图函数来输出位图到设备上下文中. BitBlt 用于从原设备中复制位图到目标设备,语 ...

  10. 高性能MySql进化论(一):数据类型的优化_上

    在数据库的性能调优的过程中会涉及到很多的知识,包括字段的属性设置是否合适,索引的建立是否恰当,表结构涉及是否合理,数据库/操作系统 的设置是否正确…..其中每个topic可能都是一个领域. 在我看来, ...