取出一行,檢查是否含有“//和/* */”註釋
如果有:
確定其位置(strstr()),用數組取出位置前面的,cout < <
否則:
cout < <
大致流程就是這樣的了吧
沒這麼簡單吧,
如果你程序裏面有字符串裏面也有/*呢
比如char* string = "//*/*// ";
總的程序思想感覺不會太難的,處理過程有點兒向編譯器中的詞法分析,只不過,這個比編譯器設計好簡單的多.只要找出你個關鍵字就可以了.
首先,讀出一行源程序代碼;
然後,按順序一個一個讀字符,若出現連續兩個 "/ "符號,且出現的位置不在一個完整的程序語句中時,可以確定爲註釋行(在C++的源代碼中);若出現的位置是一個不完整的程序語句時,需要判斷是否是處在一個字符串中,若不是,則連續 "/ "符號之後的所有字符都是註釋;若是,則可能是程序中使用的字符串,不能刪除. 對於/* */結構來說,要好處理一些,只要找到 "/* "就可以了,找到之後,將其後所有的字符都假設爲註釋字符,一直遇到 "*/ "爲止.
最後,就是在上面一步處理的同時,講有用的程序代碼保存起來.
我寫過一個去除註釋的模塊,應該包含了我所想到的所有情況。
雖然不能提供代碼(出於職業道德),不過可以提供一下思路。
1先列出註釋的所有可能情況,比如printf( "/*abc*/ ")就不能算註釋,還有很多
這種情況,
2 畫好狀態轉換圖,確定讀入特定的字符之後,下一步應該判斷哪個字符。比如,讀如了/之後,下一步
判斷是否是/。如果是的話,把後面同行的內容刪掉。如果是*的話,就一直刪到下一對字符*/
#include <iostream>
using namespace std;
int main()
{
char c;
while(cin.get(c))
{if (c== ' " ')
{cout < <c;
while(cin.get(c))
{if(c== ' " ')break;
cout < <c;
}
}
if (c== '/ ' ')
{cout < <c;
while(cin.get(c))
{if(c== '/ ' ')break;
cout < <c;
}
}
if(c== '/ ')
{
cin.get(c);
if(c== '* ')
{
cout < < ' ';
while(cin.get(c))
{
if(c== '* ')
{
cin.get(c);
if(c!= '/ ')cin.putback(c);
else break;
}
}
}
else if(c== '/ ')
{
while(cin.get(c))
if(c== '/n '){cout < < '/n ';break;}
}
else cout < < '/ ' < <c;
}
else cout < <c;
}
}
我寫了一個,但似乎有點問題,先不考慮用bat文件erasecomments <source.cpp > target.cpp,將source.cpp轉換成target.cpp。至考慮從屏幕輸入輸出的問題,比如輸入 "dfaf/*fj;lsaf*/,但輸出的仍是 "dfaf/*fj;lsaf*/,雖說在C++語法中,這種東西是不會存在的,但我困惑的是爲什麼會出現這種情況,而且在輸入完類似 "dfaf/*fj;lsaf*/的語句後,程序就會失效,譬如輸入/*abcd*/,卻依然輸出/*abcd*/,但程序如果沒有輸入過類似 "dfaf/*fj;lsaf*/的語句,程序是完全有效的。
|
||
if(!*from) return true;//文件結束
可以修改爲:
if(!*from){
if(S==Normal) return true;
return false;
}
今天閒逛Linux寶庫,看到論壇裏有人在討論如何用 shell 腳本來處理 C 語言註釋,發帖時間是 08-10-23(以前怎麼都沒注意到,失敗...),但問題好像並沒被解決。正好這兩天玩 sed & awk,來小試一下身手。
C語句註釋
本文討論的是 C99 標準,它支持單行註釋(“// ...”)和塊註釋(“/*...*/”),並且當單行註釋以“/”結尾時也可以跨多行。測試代碼如下:
#include <stdlib.h>
#include <stdio.h>
int main (int argc, char *argv[])
{
// not show/
not show/
not show
// not show
/* not show */
int is; // not show
int/* not show */ ms; /* not show */
double ds; // not show/
not show/
not show
double dm; /* ...
not show
not show */ float fs; /**
* now show
*/
float/**/ fm;
char cs[] = "aaa // /***/";
char cm1[] = /* not show */"hello*/";
char cm2[] = "/*redraiment"/* not show */;
/* printf("/////"); */
return EXIT_SUCCESS;
}
#include <stdlib.h>
#include <stdio.h>
int main (int argc, char *argv[])
{
// not show/
not show/
not show
// not show
/* not show */
int is; // not show
int/* not show */ ms; /* not show */
double ds; // not show/
not show/
not show
double dm; /* ...
not show
not show */ float fs; /**
* now show
*/
float/**/ fm;
char cs[] = "aaa // /***/";
char cm1[] = /* not show */"hello*/";
char cm2[] = "/*redraiment"/* not show */;
/* printf("/////"); */
return EXIT_SUCCESS;
}
其中綠色部分就是註釋,經過處理後需要將它們全部移除或用替換成空字符。論壇原帖中沒有處理以“/”結尾的單行註釋,也沒處理註釋關鍵字出現在字符串中的情況。
工具的選擇
sed 是一個流編輯器,它能對文件進行“插入”、“刪除”、“替換”、“追加”等編輯操作;而 awk 是一門模式匹配的程序設計語言,它除了能編輯文本還可以統計信息,你可以把它看成基於文本文件的數據庫系統。原帖中作者使用 sed 來解決,因爲問題涉及的操作僅僅是刪除 C 代碼中的註釋。但由於以下原因導致 sed 心有餘而力不足:
一、不支持最小匹配
正則表達式默認採用貪心匹配策略,在正則的標準中通過在量詞後面加“?”來使用最小匹配策略,詳細規則介紹請參見這裏。問題中多行註釋必須使用最小匹配原則,如果關鍵詞只有一個字符,就可以通過排除字符集來模擬,比如我們經常用“"[^"]*"”來匹配一個字符串。可惜 C 語言的註釋關鍵詞都是多字符。
二、排序字符(Collating Symbols)只是一個美麗的夢想
排序字符用於字符列表(character list)中。按照文檔的描述,排序字符可將多個字符當一個字符來匹配。比如模式“[[.ch.]i]”可以匹配行“char”和“int”,但不能匹配“coho”。如果它能被支持,就可以把“/*”、“*/”、“//”都看做一個字符,通過(一)中的排除字符集來實現最小匹配。可惜到目前爲止,我接觸的工具中沒有一款支持這個特性,更不用說對正則支持平平的 sed 了。
三、字符串來搗亂
在《sed單行腳本學習筆記》中我給出了一段 sed 單行腳本,用於替換不在字符串中的模式。看起來正適合解決這個問題,但它的前提是模式要和待修改的文本完全匹配。由(一)、(二)兩個條件決定了 sed 實現的正則表達式無法匹配 C 語言所有類型的註釋。另外,sed 提供的控制語句是“b、t”,它們的功能是類似於 C 語言的 goto,因此它不能像“if ... else ...”一樣方便地判斷某個註釋的起始位置是否在字符串中。
由於上述原因,我們需要一個變量來記錄當前狀態——是否在字符串中。因此我使用 awk 來解決。
我的解決方案
# filename: strip_c_comment.awk
# issue: awk -f scrip_c_comment.awk test.c
BEGIN { FS="" }
!(ignore_line && $NF == "//") && !ignore_line-- {
ignore_line = 0;
for(i = 1; i <= NF; i++) {
if (ignore_block) {
if ($i $(i+1) == "*/") {
ignore_block = 0
i++ # remove '*'
}
continue
}
if (!instr && $i $(i+1) == "/*") {
ignore_block = 1