状态机编程思想(2):删除代码注释(目前支持C/C++和Java)
有时为了信息保密或是单纯阅读代码,我们常常需要删除注释。
之前考虑过正则表达式,但是感觉实现起来相当麻烦。而状态机可以把多种情况归为一类状态再行分解,大大简化问题。本文就是基于状态机实现的。
删除C/C++代码注释
需要考虑的情况
- //
- /* */
- //和/* */嵌套(注意不存在/* */和/* */嵌套)
- 折行注释(用\间隔)
- 字符中存在的/和*
- 字符串中存在的//和/* */
- 字符串中的折行代码(用\间隔)
- 头文件中可能存在的/
状态转移描述
这里的内容参考了博客http://www.cnblogs.com/zhanghaiba/p/3569928.html#3853787,写得很赞。
本文基于上面所述博文进行了以下修改或是优化:
- 改成了java代码
- 切换到了windows平台下,支持windows换行(注意:如果原文件末尾没有回车,会自动插入)
- 状态量优化为枚举常量
- 状态转移由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 { 85 state=State.NOTE_MULTILINE; 86 } 87 break; 88 case NOTE_SINGLELINE: 89 if(c=='\\') { 90 state=State.BACKSLASH; 91 }else if(c=='\n'){ 92 builder.append("\r\n"); 93 state=State.CODE; 94 }else { 95 state=State.NOTE_SINGLELINE;//保持当前状态,可省略本句 96 } 97 break; 98 case BACKSLASH: 99 if(c=='\\' || c=='\r'||c=='\n') {//windows系统换行符为\r\n 100 if(c=='\n') { 101 builder.append("\r\n");//保留空行,当然,也可以去掉 102 } 103 state=State.BACKSLASH;//保持当前状态,可省略本句 104 }else { 105 state=State.NOTE_SINGLELINE; 106 } 107 break; 108 case CODE_CHAR: 109 builder.append(c); 110 if(c=='\\') { 111 state=State.CHAR_ESCAPE_SEQUENCE; 112 }else if(c=='\'') { 113 state=State.CODE; 114 } 115 break; 116 case CHAR_ESCAPE_SEQUENCE: 117 builder.append(c); 118 state=State.CODE_CHAR; 119 break; 120 case CODE_STRING: 121 builder.append(c); 122 if(c=='\\') { 123 state=State.STRING_ESCAPE_SEQUENCE; 124 }else if(c=='\"') { 125 state=State.CODE; 126 } 127 break; 128 case STRING_ESCAPE_SEQUENCE: 129 builder.append(c); 130 state=State.CODE_STRING; 131 break; 132 default: 133 break; 134 } 135 } 136 return builder.toString(); 137 } 138 139 /** 140 * @function 从指定文件中读取代码内容,以String形式返回 141 * @param inputFileName 待删除注释的文件 142 * @return 待删除注释的文件中的代码内容,String字符串形式 143 * @note 输入文件格式默认为 UTF-8 144 */ 145 public static String readFile(String inputFileName) { 146 StringBuilder builder = new StringBuilder(); 147 try { 148 FileInputStream fis = new FileInputStream(inputFileName); 149 InputStreamReader dis = new InputStreamReader(fis); 150 BufferedReader reader = new BufferedReader(dis); 151 String s; 152 // 每次读取一行,当改行为空时结束 153 while ((s = reader.readLine()) != null) { 154 builder.append(s); 155 builder.append("\r\n");// windows系统换行符 156 } 157 dis.close(); 158 } catch (IOException e) { 159 e.printStackTrace(); 160 System.exit(1); 161 } 162 return builder.toString(); 163 } 164 165 /** 166 * @function 将删除注释后的代码保存到指定新文件 167 * @param outputFileName 保存“删除注释后的代码”的文件的文件名 168 * @param strHandled 删除注释后的代码 169 */ 170 public static void writeFile(String outputFileName, String strHandled) { 171 try { 172 FileOutputStream fos = new FileOutputStream(outputFileName); 173 OutputStreamWriter dos = new OutputStreamWriter(fos); 174 BufferedWriter writer = new BufferedWriter(dos); 175 writer.write(strHandled); 176 writer.close(); 177 System.out.println("code that without note has been saved successfully in " + outputFileName); 178 } catch (IOException e) { 179 e.printStackTrace(); 180 } 181 } 182 183 /** 184 * @function 读取待处理文件,删除注释,处理过的代码写入新文件 185 * @param args 186 */ 187 public static void main(String[] args) { 188 Scanner in = new Scanner(System.in); 189 //待删除注释的文件 190 System.out.println("please input fileName that will be delete note:"); 191 String inputFileName = in.nextLine(); 192 //保存“删除注释后的代码”的文件 193 System.out.println("please input fileName that will save code without note:"); 194 String outputFileName = in.nextLine(); 195 196 String strToHandle = readFile(inputFileName); 197 String strHandled = delete_C_Cplusplus_Java_Note(strToHandle); 198 writeFile(outputFileName, strHandled); 199 200 } 201 202 }
说明
- 本程序保留注释占用行,也就是说,注释以外的代码原样保留(行数也不会变),注释行变为空白。
- 不检测文件后缀(这就意味着把代码写在.txt里面也可以处理),有需求的可以自行添加。
- 文件格式默认为UTF。
- 有兴趣的可以封装成图形界面,直接拖入文件处理,更好用。
参考
- 怎样删除C/C++代码中的所有注释?浅谈状态机的编程思想: http://www.cnblogs.com/zhanghaiba/p/3569928.html#3853787
- 谁能写出个删除注释的正则表达式:http://bbs.csdn.net/topics/380183706
- 正则表达式删除代码的注释: http://blog.csdn.net/conquer0715/article/details/14446463