状态机编程思想(2):删除代码注释(目前支持C/C++和Java)

前言

有时为了信息保密或是单纯阅读代码,我们需要删除注释。

之前考虑过正则表达式,但是感觉实现起来相当麻烦。而状态机可以把多种情况归为一类状态再行分解,大大简化问题。本文就是基于状态机实现的。

目录

  • 删除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\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 }

 

说明

  • 本程序保留注释占用行,也就是说,注释以外的代码原样保留(行数也不会变),注释行变为空白。
  • 不检测文件后缀(这就意味着把代码写在.txt里面也可以处理),有需求的可以自行添加。
  • 本程序适用于windows平台,其他平台如linux和mac请替换“\r\n”换行符。文件格式默认为UTF。
  • 有兴趣的可以封装成图形界面,直接拖入文件处理,更好用。
  • 本程序经过大量测试未发现bug,若读者发现bug,欢迎提出。

 

参考

posted @ 2017-12-01 12:28  xiaoxi666  阅读(1619)  评论(2编辑  收藏  举报
TOP