问题来源,在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. CentOS卸载openoffice

    rpm -e `rpm -qa |grep openoffice` `rpm -qa |grep ooobasis` 这样算是比较彻底的

  2. [转]Java Daemon Startup Script

    How to start a Java program as a Linux daemon with an /etc/init.d script. See also: http://www.sourc ...

  3. 转载:c++内存泄露机制

    对于一个c/c++程序猿来说,内存泄漏是一个常见的也是令人头疼的问题.已经有很多技术被研究出来以应对这个问题,比方 Smart Pointer,Garbage Collection等.Smart Po ...

  4. QP01 BAPI、QP02 BDC

    近期在改动一个检验计划分配的一个程序.上网查了一些资料,分别对QP01检验计划创建.改动QP02.删除物料等操作.分享一下. 一.QP01 BAPI BAPI_INSPECTIONPLAN_CREAT ...

  5. JDK动态代理实现简单AOP--转

    JDK 动态代理是 java 反射的一个重要特性.它在某种方面为 java 提供了动态性的特性,给应用带来了无限的空间.大名鼎鼎的 Hessian . Spring AOP 基于动态代理实现.本文将简 ...

  6. 《UNIX网络编程》之read_timeout实验

    最近在做项目,需要做一个服务器和客户端的基于TCP的套接口网络编程,由于服务器端返回数据并不是那么的及时,因此,需要在客户端做些延迟,然后才能去读取数据,实验测试结果如下. 首先,我们先来看一下我们封 ...

  7. 【Android】无限滚动的HorizontalScrollView

    这是一个很简单的功能,作为新手,做一下笔记.也给其它有需要的人提供一个参考. 首先HorizontalScrollView都知道用途了.它可以实现类似“桌面程序”左右切换页的效果.一般情况下里面的页数 ...

  8. Java基础知识强化26:Object类之hashCode()方法、getClass()方法

    1. Object类的hashCode()方法,如下: public  int  hashCode():返回该对象的哈希码值,这个值和地址值有关,但是不是实际地址值(哈希码值是根据实际地址值转化过来的 ...

  9. Sorting File Contents and Output with sort

     Sorting File Contents and Output with sort   Another very useful command to use on text file is  so ...

  10. 自定义控件 TextView 歌词 Lrc

    演示 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> < ...