狀態機器編程思想(2):刪除代碼注釋(目前支援C/C++和Java),編程思想java
前言
有時為了資訊保密或是單純閱讀代碼,我們需要刪除注釋。
之前考慮過Regex,但是感覺實現起來相當麻煩。而狀態機器可以把多種情況歸為一類狀態再行分解,大大簡化問題。本文就是基於狀態機器實現的。
目錄
- 刪除C/C++代碼注釋
- 刪除Java代碼注釋
- 程式
- 參考
刪除C/C++代碼注釋需要考慮的情況
- //
- /* */
- //和/* */嵌套(注意不存在/* */和/* */嵌套)
- 折行注釋(用\間隔)
- 字元中存在的/和*
- 字串中存在的//和/* */
- 字串中的折行代碼(用\間隔)
- 標頭檔中可能存在的/
狀態轉移描述
思路參考了部落格http://www.cnblogs.com/zhanghaiba/p/3569928.html#3853787,寫得很贊。
本文基於上面所述博文進行了以下修改或是最佳化:
- 原博文沒有考慮/***/的情況(其中*的個數為奇數),已修正
- 切換到了windows平台下,支援windows換行\r\n(並請注意:如果原檔案末尾沒有斷行符號,會自動插入)
- 狀態量最佳化為枚舉常量
- 狀態轉移由if...else...elseif結構改為switch...case結構,更為清晰,對於大型代碼,效率更高
其中,除狀態NOTE_MULTILINE_STAR外,其餘狀態下均需進行字元(串)處理,以保持正確輸出。詳見文末代碼。
刪除Java代碼注釋需要考慮的情況
- //
- /* */
- /** */
- //和/**/嵌套(注意不存在/* */和/* */嵌套,不存在/** */和/** */嵌套,不存在/* */和/** */嵌套)
- //和/** */嵌套
- 字元中存在的/和*
- 字串中存在的//、/**/以及/** */
狀態轉移描述
可以看到,java中的注釋規則更為簡單,其中/** */完全可以用/* */的狀態涵蓋。且不會出現折行注釋和字串折行的情況,因此狀態更加簡單,有興趣的可以畫一畫,這裡就不畫圖了。換句話說,上面刪除C/C++注釋的程式完全可以用來刪除java注釋。
程式
1 import java.io.FileInputStream; 2 import java.io.InputStreamReader; 3 import java.io.BufferedReader; 4 5 import java.io.FileOutputStream; 6 import java.io.OutputStreamWriter; 7 import java.io.BufferedWriter; 8 9 import java.io.IOException; 10 11 import java.util.Scanner; 12 13 /** 14 * @author xiaoxi666 15 * @version 1.0.0 2017.12.01 16 */ 17 18 public class deleteCAndCplusplusAndJavaNote { 19 20 /** 21 * 狀態 22 */ 23 enum State { 24 CODE, // 正常代碼 25 SLASH, // 斜杠 26 NOTE_MULTILINE, // 多行注釋 27 NOTE_MULTILINE_STAR, // 多行注釋遇到* 28 NOTE_SINGLELINE, // 單行注釋 29 BACKSLASH, // 折行注釋 30 CODE_CHAR, // 字元 31 CHAR_ESCAPE_SEQUENCE, // 字元中的逸出字元 32 CODE_STRING, // 字串 33 STRING_ESCAPE_SEQUENCE// 字串中的逸出字元 34 }; 35 36 /** 37 * @function 刪除代碼中的注釋,以String形式返回 38 * @param strToHandle 待刪除注釋的代碼 39 * @return 已刪除注釋的代碼,String字串形式 40 */ 41 public static String delete_C_Cplusplus_Java_Note(String strToHandle) { 42 StringBuilder builder = new StringBuilder(); 43 44 State state = State.CODE;// Initiate 45 for (int i = 0; i < strToHandle.length(); ++i) { 46 char c = strToHandle.charAt(i); 47 switch (state) { 48 case CODE: 49 if (c == '/') { 50 state = State.SLASH; 51 }else { 52 builder.append(c); 53 if(c=='\'') { 54 state=State.CODE_CHAR; 55 }else if(c=='\"') { 56 state=State.CODE_STRING; 57 } 58 } 59 break; 60 case SLASH: 61 if (c == '*') { 62 state = State.NOTE_MULTILINE; 63 } else if (c == '/') { 64 state = State.NOTE_SINGLELINE; 65 } else { 66 builder.append('/'); 67 builder.append(c); 68 state = State.CODE; 69 } 70 break; 71 case NOTE_MULTILINE: 72 if(c=='*') { 73 state=State.NOTE_MULTILINE_STAR; 74 }else { 75 if(c=='\n') { 76 builder.append("\r\n");//保留空行,當然,也可以去掉 77 } 78 state=State.NOTE_MULTILINE;//保持目前狀態 79 } 80 break; 81 case NOTE_MULTILINE_STAR: 82 if(c=='/') { 83 state=State.CODE; 84 }else if(c=='*') { 85 state=State.NOTE_MULTILINE_STAR;//保持目前狀態 86 } 87 else { 88 state=State.NOTE_MULTILINE; 89 } 90 break; 91 case NOTE_SINGLELINE: 92 if(c=='\\') { 93 state=State.BACKSLASH; 94 }else if(c=='\n'){ 95 builder.append("\r\n"); 96 state=State.CODE; 97 }else { 98 state=State.NOTE_SINGLELINE;//保持目前狀態 99 }100 break;101 case BACKSLASH:102 if(c=='\\' || c=='\r'||c=='\n') {//windows系統分行符號為\r\n103 if(c=='\n') {104 builder.append("\r\n");//保留空行,當然,也可以去掉105 }106 state=State.BACKSLASH;//保持目前狀態107 }else {108 state=State.NOTE_SINGLELINE;109 }110 break;111 case CODE_CHAR:112 builder.append(c);113 if(c=='\\') {114 state=State.CHAR_ESCAPE_SEQUENCE;115 }else if(c=='\'') { 116 state=State.CODE;117 }else {118 state=State.CODE_CHAR;//保持目前狀態119 }120 break;121 case CHAR_ESCAPE_SEQUENCE:122 builder.append(c);123 state=State.CODE_CHAR;124 break;125 case CODE_STRING:126 builder.append(c);127 if(c=='\\') {128 state=State.STRING_ESCAPE_SEQUENCE;129 }else if(c=='\"') { 130 state=State.CODE;131 }else {132 state=State.CODE_STRING;//保持目前狀態133 }134 break;135 case STRING_ESCAPE_SEQUENCE:136 builder.append(c);137 state=State.CODE_STRING;138 break;139 default:140 break;141 }142 }143 return builder.toString();144 }145 146 /**147 * @function 從指定檔案中讀取代碼內容,以String形式返回148 * @param inputFileName 待刪除注釋的檔案149 * @return 待刪除注釋的檔案中的代碼內容,String字串形式150 * @note 輸入檔案格式預設為 UTF-8151 */152 public static String readFile(String inputFileName) {153 StringBuilder builder = new StringBuilder();154 try {155 FileInputStream fis = new FileInputStream(inputFileName);156 InputStreamReader dis = new InputStreamReader(fis);157 BufferedReader reader = new BufferedReader(dis);158 String s;159 // 每次讀取一行,當改行為空白時結束160 while ((s = reader.readLine()) != null) {161 builder.append(s);162 builder.append("\r\n");// windows系統分行符號163 }164 reader.close();165 dis.close();166 fis.close();167 } catch (IOException e) {168 e.printStackTrace();169 System.exit(1);170 }171 return builder.toString();172 }173 174 /**175 * @function 將刪除注釋後的代碼儲存到指定新檔案176 * @param outputFileName 儲存“刪除注釋後的代碼”的檔案的檔案名稱177 * @param strHandled 刪除注釋後的代碼178 */179 public static void writeFile(String outputFileName, String strHandled) {180 try {181 FileOutputStream fos = new FileOutputStream(outputFileName);182 OutputStreamWriter dos = new OutputStreamWriter(fos);183 BufferedWriter writer = new BufferedWriter(dos);184 writer.write(strHandled);185 writer.close();186 dos.close();187 fos.close();188 System.out.println("code that without note has been saved successfully in " + outputFileName);189 } catch (IOException e) {190 e.printStackTrace();191 }192 }193 194 /**195 * @function 讀取待處理檔案,刪除注釋,處理過的代碼寫入新檔案196 * @param args197 */198 public static void main(String[] args) {199 Scanner in = new Scanner(System.in);200 //待刪除注釋的檔案201 System.out.println("The fileName that will be delete note:");202 String inputFileName = in.nextLine();203 //儲存“刪除注釋後的代碼”的檔案204 System.out.println("The fileName that will save code without note:");205 String outputFileName = in.nextLine();206 207 String strToHandle = readFile(inputFileName);208 String strHandled = delete_C_Cplusplus_Java_Note(strToHandle);209 writeFile(outputFileName, strHandled);210 211 }212 213 }
說明
- 本程式保留注釋佔用行,也就是說,注釋以外的代碼原樣保留(行數也不會變),注釋行變為空白。
- 不檢測檔案尾碼(這就意味著把代碼寫在.txt裡面也可以處理),有需求的可以自行添加。
- 本程式適用於windows平台,其他平台如linux和mac請替換“\r\n”分行符號。檔案格式預設為UTF。
- 有興趣的可以封裝成圖形介面,直接拖入檔案處理,更好用。
- 本程式經過大量測試未發現bug,若讀者發現bug,歡迎提出。
參考
- 怎樣刪除C/C++代碼中的所有注釋?淺談狀態機器的編程思想: http://www.cnblogs.com/zhanghaiba/p/3569928.html#3853787
- 誰能寫出個刪除注釋的Regex:http://bbs.csdn.net/topics/380183706
- Regex刪除代碼的注釋: http://blog.csdn.net/conquer0715/article/details/14446463