本文的目的在于探讨算法,纯属娱乐和活跃脑细胞。所以,不对删除注释的目的进行讨论:)这是曾经遇到的一道面试题,可惜当时对于题目的理解不到位,导致最后的解法有误。最近有空,静下心来仔细思考了一下,将解法通过BLOG记录下来。
首先,分析一下Java中注释的类型:
/*type1*/ /* *type2 */ //type3
实际上,type1和type2是同种类型,只是表现上来看,type2中包含了换行符。
最直接的解法,就是记录注释的开始位置,然后等到注释结束时进行删除操作。伪代码如下:
for(输入文本) if(/*)记录type1类型注释开始位置; else if(//)记录type2注释开始位置; else if(*/)根据type1的开始位置,删除注释; else if(\n)根据type3的开始位置,删除注释;
看起来问题就这么简单地解决了。然而,我们却没有考虑以下特殊情况:
System.out.println("/*In Mark*/");
代码中的字符串中包含了注释是不能删除的,这下问题有些棘手了。需要判断当前进行删除的注视是否位于字符串之中,所以伪代码修改如下:
for(输入文本) if(/*) if(引号开始)continue; else 记录type1类型注释开始位置; else if(//) if(引号开始)continue; else 记录type3注释开始位置; else if(*/) 根据type1的开始位置,删除注释; else if(\n) 根据type3的开始位置,删除注释; else if(") if(引号开始)结束引号; else 记录引号开始;
完全解决问题了吗?如果引号之中还有一个嵌套的引号,是否会影响对引号开始的判断呢?
当然,Java中,嵌套引号是需要引入转义符的。所以,在判断引号时,再判断一下是否为转义的引号,如果是,则不进行引号的相关处理。
分析到此为止,下面是实现的代码:
import java.io.BufferedReader; import java.io.File; import java.io.FileReader; /** * @author Insunny * */ public class DelCommentsInJava { private static final char MARK = '"'; private static final char SLASH = '/'; private static final char BACKSLASH = '\\'; private static final char STAR = '*'; private static final char NEWLINE = '\n'; //引号 private static final int TYPE_MARK = 1; //斜杠 private static final int TYPE_SLASH = 2; //反斜杠 private static final int TYPE_BACKSLASH = 3; //星号 private static final int TYPE_STAR = 4; // 双斜杠类型的注释 private static final int TYPE_DSLASH = 5; /** * 删除char[]数组中_start位置到_end位置的元素 * * @param _target * @param _start * @param _end * @return */ public static char[] del(char[] _target, int _start, int _end) { char[] tmp = new char[_target.length - (_end - _start + 1)]; System.arraycopy(_target, 0, tmp, 0, _start); System.arraycopy(_target, _end + 1, tmp, _start, _target.length - _end - 1); return tmp; } /** * 删除代码中的注释 * * @param _target * @return */ public static String delComments(String _target) { int preType = 0; int mark = -1, cur = -1, token = -1; // 输入字符串 char[] input = _target.toCharArray(); for (cur = 0; cur < input.length; cur++) { if (input[cur] == MARK) { // 首先判断是否为转义引号 if (preType == TYPE_BACKSLASH) continue; // 已经进入引号之内 if (mark > 0) { // 引号结束 mark = -1; } else { mark = cur; } preType = TYPE_MARK; } else if (input[cur] == SLASH) { // 当前位置处于引号之中 if (mark > 0) continue; // 如果前一位是*,则进行删除操作 if (preType == TYPE_STAR) { input = del(input, token, cur); // 退回一个位置进行处理 cur = token - 1; preType = 0; } else if (preType == TYPE_SLASH) { token = cur - 1; preType = TYPE_DSLASH; } else { preType = TYPE_SLASH; } } else if (input[cur] == BACKSLASH) { preType = TYPE_BACKSLASH; } else if (input[cur] == STAR) { // 当前位置处于引号之中 if (mark > 0) continue; // 如果前一个位置是/,则记录注释开始的位置 if (preType == TYPE_SLASH) { token = cur - 1; } preType = TYPE_STAR; } else if(input[cur] == NEWLINE) { if(preType == TYPE_DSLASH) { input = del(input, token, cur); // 退回一个位置进行处理 cur = token - 1; preType = 0; } } } return new String(input); } /** * @param args */ public static void main(String[] args) { try { File file = new File("./src/Test.java"); BufferedReader reader = new BufferedReader(new FileReader(file)); StringBuilder content = new StringBuilder(); String tmp = null; while ((tmp = reader.readLine()) != null) { content.append(tmp); content.append("\n"); } String target = content.toString(); System.out.println(delComments(target)); } catch (Exception e) { } } }
还有别的解决方案吗?能否用正则表达式来实现?继续思考中...