统计注释行 下载本文

统计代码注释行的一些问题解析

(我只是一个C++初学者,对一些问题的想法肯定存在错误的认识,仅仅提出一些我当时的 思考)大家可以直接看文章最后的方法即可。

1. 要统计一个c文件或者cpp文件里面的注释行,空行,总行数等,首先我们遇到的问题就是打开这个文件。

ofstream: 写操作(输出)的文件类

ifstream: 读操作(输入)的文件类

fstream: 可同时读写操作的文件类

这里我们只要读取文件里面的数据即可,所以选用ifstream.注意头文件(fstream).

然后我们也许想,我想打开指定位置的文件,应该怎么做,怎么把指定目录下的c文件和cpp文件显示出来让我们选择。这里我们就要用到一些“dos”命令了。

(不要问我为什么给dos加上双引号,这个??上次看到一个博文,说把那个黑框框称为dos是多么业余,多么可笑的称呼,所以??你懂的,其实我还不太理解,嘻嘻)

1. ifstream infile;

2. system("dir /D file\\*.c*");

3. cout << "please enter a file name: ";

4. char filename[50];

5. cin >> filename;

6. char path[100] = "E:\\VS workplace\\C++ place\\CTEXT\\CTEXT\\file\\";

7. strcat_s(path, 100, filename);

8. infile.open(path);

if (!infile)

{

cout << "open file error!" << endl;

exit(1);

}

第一行就不用多说了,第二行就是显示当前工作路径的file文件下面的c文件和cpp文件,*是通配符。System就是调用一些dos命令用的,记得加头文件(windows.h),dir就是列出目录下面的文件,/D是dir的选项,这样列出的文件更加看一些,当然你可以选其他的选项,选项后面就是文件路径了,记得路径的反斜杠要有转义字符哦,两个反斜杠。

文件名当然要我们自己输入了,所以3,4,5行就是干这个用的。6,7行是把要打开的文件名和路径连接起来。Strcat(strcat_s更安全一些,比如你连接后的字符串超过预定空间了,编译不会提示错误,只要执行时才出错,这个我也不太懂)连接两个字符串,头文件(cstring).第8行就是打开文件喽。infile.open(path),path后面还可以写上选项。

ios::in 为输入(读)而打开文件

ios::out 为输出(写)而打开文件

ios::ate 初始位置:文件尾

ios::app 所有输出附加在文件末尾

ios::trunc 如果文件已存在则先删除该文件

ios::binary 二进制方式

例如(infile.open(path, ios::binary)),以二进制方式打开。

后面几行就是判错了,看看是否正确打开。

(打开文件的方法当然不止我这样写,选一种自己看着顺眼的即可,比如ifstream infile(”filename”)).接下来进入我们的重点环节。

2. 文件打开了,这次就要开始读里面的数据了。

分析:a. 要统计注释行,即出现这些符号的行,”//”,”/* */”。

b. 要统计空行,即遇到换行符都没有一个字符。

c. 要统计总行数,定义一个静态变量,每读取一行前,加加即可。

d. 统计代码行。

我们注意到,那些注释符都是由两个字符组成的。

1,如果我们每次从文件中读取一个字符,那么,我们就需要一前一后标记两个字符,就要打开文件两次,定义两个指针。

2,如果我们每次读取一行,那么就不用那么麻烦,统计总行数也非常方便。但同时出现的问题就是,每次读取出来的数据要存起来,然后在里面找那些注释符号。

思考:注释出现的情况比较多,例如下面这些注释行

1. // xxxxxxxxx 单行行注释

2. /* xxxxxxxxxx 单行块注释 */

3. /* xxxxxxxxxxxxx 多行块注释

Xxxxxxxx

Vvvvvvvvvv

*/

4. /* // 单行块注释里面出现行注释符号。 */

5. /* xxxxxxxxxx

Vvvvvvvvvvv 多行块注释出现行注释

// ccccccc

*/

7. xxxxxxxxxxxxxxx //vvv 前代码,后注释

8.??

首先我们看看这些情况,通过观察我们发现,我们要记录两个数据,第一是注释符本身,第二是注释符出现的位置(.一行里面,比较先出现块注释符号还是行注释符号// /* ) 如果是行注释”//”在前,块注释在后面,则忽略块注释” /* ”。如果先有块注释 ” /* ”,则要忽略块注释里面的全部行注释,不予计算。

如果我们采用一个一个字符的方法读数据,我只想说“我滴神呀!,要记录的东西太麻烦了!”,好吧,咱们还是用每次读取一行的方法吧。

每次读取一行,然后在里面找相应匹配的字符,然后?? ,等等,刚才我们说啥? 匹配字符!这个??,我们可以写一个函数,然后在里面找子字符串呀! 找这些 “//, /* , */”,

同时,我们可以返回那些子字符串出现的位置,然后??几乎所有问题都解决了!好了,我们看看这个函数。

这个相信大家都能看懂的,不做解释了。

我们说点题外话,关于我当时读取文件内容时出现的问题。

while (!infile.eof())

{

infile.read(&ch, 1);

cout << ch;

}

看看输出结果,怎么有点不对呢?

最后一个字符数被读取了两次???????????然后我百度了一下,觉得这个解释还能说的通。

事实上,文件本身是没有文件结束符EOF的。当读取文件中最后一个有效字符后,虽然文件指针已指向空白了,但这时还不知道是否到了文件末尾,只有再读取一次文件,待读不到任何内容了,这时输入流设置eofbit位,eof的返回值才为l,而空的内容是不会被提取到变量的,故最后一次读到变量中的内容又被重复输出了。

while (infile.read(&ch, 1))

{

//infile.read(&ch, 1);

cout << ch;

}

3.接下来就是见证奇迹的时刻了,咱们逐一分析相应的情况应该怎么做。

首先我们要定义一些静态变量记录这些出现的情况了,当然刚开始不可能想的那么完全,我也是需要什么然后才定义的。

while (getline(infile, s))

{

++count; //每读取一行,总行数加一

char* p;

p = new char[s.length() + 1]; //分配空间存储读取的数据

strcpy_s(p, s.length() + 1, s.c_str());

//如果读取某行的字符串长度为0,那么这行肯定就是空行了。 string s;//存储读取的字符 static int count = 0; //总行数 static int commLines = 0; //注释行 static int massiveline = 0;//块注释行 static int sub_commLines = 0;// 若"/* */" 之间的// 应该减掉 // /* */ static int all_comm = 0;//只有注释 static int nullcodeLines = 0;//空行 static int AccoutStar1 = 0;// 记录 /* 的次数 static int markslines = 0; //记录 双引号里面的注释行 static bool marksflag=true;//匹配双引号