C++統計代碼注釋行數 & 有效代碼行數 & 代碼注釋公共行 & 函數個數
問題來源,在14年的暑假的一次小項目當中遇到了一個這樣的問題,要求統計C++代碼的注釋行數,有效代碼行數,代碼注釋公共行數,以及函數個數。
下面稍微解釋一下問題,
1)注釋行數:指有注釋的行,包括有代碼和注釋的公共行(如:3,4,15,22...)
2)有效代碼行:指有代碼的行,包括有代碼和注釋的公共行(如:1,4,11,15,25....)
3)代碼注釋公共行:指又有代碼又有注釋的行(如:4,15...)
4)函數個數:這個不用說明了吧。
以下為注釋情況展示代碼:
復制代碼
1 #include <stdio.h>
2
3 //follow is a common line
4 void swap(char *p/* = NULL */)
5 {
6 printf("this is a function /*is not comments*/");
7 }
8
9 int main(void)
10 {
11 int a = 10;
12 int b;
13 char *p = NULL;
14
15 swap(p); //common line
16 if (10 == a)
17 {
18 printf("is not function;//is not comments");
19 }
20 //pure comments/*no use*/
21
22 /*a = 5;
23 printf("not use\n");*/
24
25 b = 3;/*i'm a comment*/ a = 5; /*comments line
26 pure //comments line;"*/......"look there*/
27 printf("test \" escape char");
28 //and the follow,maybe affect the count of function
29 if (*p == '{')
30 {
31 printf("the '{' is a question\n");
32 }
33 //
34
35 return 0;
36 }
復制代碼
上面的這個代碼片段,已經基本上展示了各種注釋可能出現的情況,接著我們來分析一下注釋出現的位置:
一、“//”雙斜槓注釋
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。
其實如下代碼的函數個數統計還有問題:
示例:對於類、結構體、枚舉體、全局數組,會被統計為一個函數。也就是說,那些在函數體外面的,但是又帶有大括號({})的代碼,都會被識別為函數。
以下為實現代碼:
復制代碼
1 #include <iostream>
2 #include <fstream>
3 #include <string>
4 using namespace std;
5
6 int main(void)
7 {
8 string line="";
9 ifstream ifs;
10 ifs.open("Test.cpp");
11 if (!ifs)
12 {
13 cout<<"文件打開失敗"<<endl;
14 exit(0);
15 }
16 //////////////////////////////////////////////
17 //標記:雙引號 斜槓星 函數 代碼 注釋
18 int bSyh = 0, bXgx = 0, bHs = -1, bCode = 0, bZs = 0;
19 // "" '' // /* {}
20 /////////////////////////////////////////////
21 //個數:空行 注釋 代碼 公共 函數
22 int i,nKh = 0, nZs = 0, nDm = 0, nGg = 0, nHs = 0;
23 //
24 while (!ifs.eof())
25 {
26 i = 0;
27 getline(ifs,line); //讀取一行文件
28 bCode = 0; //該行沒有代碼
29 bZs = 0; //該行沒有注釋
30 if (bXgx) //bXgx 斜槓星注釋標記
31 bZs = 1; //該行有注釋
32 //過濾無效符號
33 while (line[i] == ' ' || line[i] == '\t' || line[i] == '\r' || line[i] == '\n')
34 {
35 ++i;
36 }
37 //“以下為空行統計區域:開始”
38 if (!bXgx && line[i] == '\0') //空行
39 {
40 ++nKh;
41 continue;
42 }
43 //“空行統計:結束”
44 while (1)
45 {
46 //第一次遇到雙引號 引號為非轉義字符(\")
47 if (!bSyh && line[i] == '\"' && ((i > 0 && line[i-1] != '\\') || (i == 0)))
48 {
49 ++i;
50 bSyh = 1;
51 continue;
52 }
53 //“正在進行雙引號屏蔽....”
54 if (bSyh)
55 {
56 //“ \”結束”
57 if (line[i] == '\"' && ((i > 0 && line[i-1] != '\\') || (i == 0)))
58 {
59 bSyh = 0;
60 }
61 else if (line[i] == '\0') //行末尾
62 {
63 if (bZs)
64 ++nZs;
65 if (bCode)
66 ++nDm;
67 if (bZs && bCode)
68 ++nGg;
69 break;
70 }
71 ++i;
72 continue;
73 }
74 //遇到單引號(避免'{','}'),且非轉義字符\',連續跳過3個(第二個'後位置)
75 if (line[i] == '\'' && ((i > 0 && line[i-1] != '\\') || (i == 0)))
76 {
77 i += 3;
78 continue;
79 }
80 //“//注釋行”
81 if (!bXgx && line[i] == '/' && line[i+1] == '/')
82 {
83 if (bCode) //“前有代碼,混合注釋行”
84 {
85 ++nZs; //注釋
86 ++nDm; //代碼
87 ++nGg; //公共
88 }
89 else //純注釋行
90 {
91 ++nZs;
92 }
93 break; //跳出當前行(即,內while循環),“//”後代碼不做判斷
94 }
95 //“/*注釋開始”
96 if (!bXgx && line[i] == '/' && line[i+1] == '*')
97 {
98 i += 2; //跳過/*符號
99 bXgx = 1; //標記“/*”開始
100 bZs = 1; //“發現注釋”
101 continue;
102 }
103 //“正在進行多行注釋....”
104 if (bXgx)
105 {
106 //“*/注釋結束”
107 if (line[i] == '*' && line[i+1] == '/')
108 {
109 ++i; //“跳過*/”注意後有一個 ++i;
110 bXgx = 0;
111 }
112 else if (line[i] == '\0') //行末尾
113 {
114 if (bCode) //注釋前有代碼,即“混合行”
115 {
116 ++nDm;
117 ++nZs;
118 ++nGg;
119 }
120 else
121 {
122 ++nZs; //“純注釋”
123 }
124 break;
125 }
126 ++i;
127 continue;
128 }
129 if (line[i] == '\0')
130 {
131 if (bZs)
132 ++nZs;
133 if (bCode)
134 ++nDm;
135 if (bZs && bCode)
136 ++nGg;
137 break;
138 }
139 //“以下全是有效代碼區域”
140 //“函數個數統計區域:開始”
141 if (line[i] == '{') //記錄函數左括號
142 {
143 ++bHs;
144 }
145 else if (line[i] == '}') //遇到函數右括號
146 {
147 if (bHs == 0) //“發現一個函數”
148 ++nHs;
149 --bHs;
150 }
151 //“函數統計:結束”
152 ++i;
153 bCode = 1; //能執行到這裡,說明該行存在代碼
154 }
155 }
156
157 cout<<"注釋: "<<nZs<<endl;
158 cout<<"代碼: "<<nDm<<endl;
159 cout<<"空行: "<<nKh<<endl;
160 cout<<"公共: "<<nGg<<endl;
161 cout<<"函數: "<<nHs<<endl;
162
163 return 0;
164 }