有时为了信息保密或是单纯阅读代码,我们需要删除注释。
之前考虑过正则表达式,但是感觉实现起来相当麻烦。而状态机可以把多种情况归为一类状态再行分解,大大简化问题。本文就是基于状态机实现的。
思路参考了博客http://www.cnblogs.com/zhanghaiba/p/3569928.html#3853787,写得很赞。
本文基于上面所述博文进行了以下修改或是优化:
其中,除状态NOTE_MULTILINE_STAR外,其余状态下均需进行字符(串)处理,以保持正确输出。详见文末代码。
可以看到,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\n
103 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-8
151 */
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 args
197 */
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 }