前言

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

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

目录

  • 删除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,欢迎提出。

参考

状态机编程思想(2):删除代码注释(目前支持C/C++和Java)的更多相关文章

  1. 《Java编程思想第四版》附录 B 对比 C++和 Java

    <Java编程思想第四版完整中文高清版.pdf>-笔记 附录 B 对比 C++和 Java “作为一名 C++程序员,我们早已掌握了面向对象程序设计的基本概念,而且 Java 的语法无疑是 ...

  2. ★ java删除代码注释

    package com.witwicky.util; import java.io.BufferedReader; import java.io.BufferedWriter; import java ...

  3. [Java编程思想-学习笔记]第3章 操作符

    3.1  更简单的打印语句 学习编程语言的通许遇到的第一个程序无非打印"Hello, world"了,然而在Java中要写成 System.out.println("He ...

  4. 面向对象的编程思想和Java中类的概念与设计

    面向对象的编程思想学习,面向对象内容的三条主线;1.java类及类的对象2.面向对象的三大特征3.其他关键字学习内容:3.1面向对象与面向过程面向对象与面向过程在应用上的区别 Java中类的概念与设计 ...

  5. 关于 java编程思想第五版 《On Java 8》

    On Java 8中文版 英雄召集令 这是该项目的GITHUB地址:https://github.com/LingCoder/OnJava8 广招天下英雄,为开源奉献!让我们一起来完成这本书的翻译吧! ...

  6. 怎样删除C/C++代码中的所有注释?浅谈状态机的编程思想

    K&R习题1-23中,要求“编写一个程序,删除C语言程序中所有的注释语句.要正确处理带引号的字符串与字符常量.在C语言中,注释不允许嵌套”. 如果不考虑字符常量和字符串常量,问题确实很简单.只 ...

  7. 撰写一篇博客要求讲述四则运算2的设计思想,源程序代码、运行结果截图、编程总结分析,并按照PSP0级的要求记录开发过程中的时间记录日志。

    一.撰写一篇博客要求讲述四则运算2的设计思想,源程序代码.运行结果截图.编程总结分析,并按照PSP0级的要求记录开发过程中的时间记录日志. 1.设计思想: ①创建test.jsp建立第一个前端界面,提 ...

  8. 函数式编程思想概述和冗余的Runnable代码

    函数式编程思想概述 在数学中,函数就是有输入量.输出量的一套计算方法 相对而言,面向对象过分强调必须通过对象的形式来做事情,而函数式的思想是尽量忽略复杂的面向对象的复杂语法--是强调做什么而不是以什么 ...

  9. Eclipse删除代码中所有注释及空格

    替换方法: Ctrl+F 删除java注释:  /\*{1,2}[\s\S]*?\*/ Ctrl+F 删除xml注释:  <!-[\s\S]*?--> Ctrl+F 删除空白行:   ^\ ...

随机推荐

  1. mybatis逆向工程

    一.背景 在实际开发中我们会自己去写mapper映射文件,接口,数据库表对应的实体类,如果需求任务比较少,咱们还可以慢慢的一个一个去写,但是这是不现实的,因为在工作中我们的任务是很多的,这时mybat ...

  2. zabbix自动截图留档_python版

    1 背景     每个DB Server都有zabbix监控,除了异常情况的报警信息外,也会在日检.周检.月检等工作中用到zabbix的监控数据,对zabbix监控数据会做两种处理:1 数据分析(环比 ...

  3. Java8之旅(七) - 函数式备忘录模式优化递归

    前言 在上一篇开始Java8之旅(六) -- 使用lambda实现Java的尾递归中,我们利用了函数的懒加载机制实现了栈帧的复用,成功的实现了Java版本的尾递归,然而尾递归的使用有一个重要的条件就是 ...

  4. [poj1644]放苹果

    题目链接:http://poj.org/problem?id=1664       把M个同样的苹果放在N个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?(用K表示)5,1,1和1,5, ...

  5. 100. Same Tree(leetcode)

    Given two binary trees, write a function to check if they are equal or not. Two binary trees are con ...

  6. MySQL索引(2)

    一.索引基础 1. B-Tree索引 <1> 所有的值都是按顺序存储的,并且每一个叶子页到根的距离相同. <2> 顺序组织存储,很适合查找范围数据,效率会非常高. <3& ...

  7. MySql 5.7.20安装

    1.首先上MySql的官网下载  https://dev.mysql.com/downloads/mysql/ 以我所选版本为例(免安装版),选择MYSQL Community Server 然后在右 ...

  8. JAVA提高十:ArrayList 深入分析

    前面一章节,我们介绍了集合的类图,那么本节将学习Collection 接口中最常用的子类ArrayList类,本章分为下面几部分讲解(说明本章采用的JDK1.6源码进行分析,因为个人认为虽然JDK1. ...

  9. javascript算法(一)

    1.实现一个函数,运算结果可以满足如下预期结果: add(1)(2) // 3 add(1, 2, 3)(10) // 16 add(1)(2)(3)(4)(5) // 15 实现: function ...

  10. 移动端二三事【三】:transform的注意事项

    1.js操作transition时需使用驼峰命名: div.style.WebkitTransform = div.style.transform = "rotate(90deg)" ...