狀態機編程思想:刪除代碼註釋(支持C/C++和Java)

前言

有時爲了信息保密或是單純閱讀代碼,咱們須要刪除註釋。
以前考慮過正則表達式,可是感受實現起來至關麻煩。而狀態機能夠把多種狀況歸爲一類狀態再行分解,大大簡化問題。本文就是基於狀態機實現的。html

刪除C/C++代碼註釋

須要考慮的狀況

  • //
  • /* */
  • //和/* */嵌套(注意不存在/* */和/* */嵌套)
  • 折行註釋(用\間隔)
  • 字符中存在的/和*
  • 字符串中存在的//和/* */
  • 字符串中的折行代碼(用\間隔)
  • 頭文件中可能存在的/
  • 狀態轉移描述

狀態轉移描述

思路參考了博客怎樣刪除C/C++代碼中的全部註釋?淺談狀態機的編程思想,寫得很贊。
本文基於上面所述博文進行了如下修改或是優化:java

  • 原博文沒有考慮/***/的狀況(其中*的個數爲奇數),已修正
  • 切換到了windows平臺下,支持windows換行\r\n(並請注意:若是原文件末尾沒有回車,會自動插入)
  • 狀態量優化爲枚舉常量
  • 狀態轉移由if...else...elseif結構改成switch...case結構,更爲清晰,對於大型代碼,效率更高

刪除代碼註釋的狀態遷移圖
刪除代碼註釋的狀態遷移圖

其中,除狀態NOTE_MULTILINE_STAR外,其他狀態下均需進行字符(串)處理,以保持正確輸出。詳見文末代碼。linux

刪除Java代碼註釋

須要考慮的狀況

  • //
  • /* */
  • /** */
  • //和/**/嵌套(注意不存在/* */和/* */嵌套,不存在/** */和/** */嵌套,不存在/* */和/** */嵌套)
  • //和/** */嵌套
  • 字符中存在的/和*
  • 字符串中存在的//、/**/以及/** */
  • 狀態轉移描述

狀態轉移描述

能夠看到,java中的註釋規則更爲簡單,其中/** */徹底能夠用/* */的狀態涵蓋。且不會出現折行註釋和字符串折行的狀況,所以狀態更加簡單,有興趣的能夠畫一畫,這裏就不畫圖了。換句話說,上面刪除C/C++註釋的程序徹底能夠用來刪除java註釋。正則表達式

程序

package code_tools;


import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;

import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.BufferedWriter;

import java.io.IOException;

import java.util.Scanner;

/** * @author xiaoxi666 * @version 1.0.0 2017.12.01 */

public class deleteCAndCplusplusAndJavaNote {

    /** * 狀態 */
    enum State {
        CODE, // 正常代碼
        SLASH, // 斜槓
        NOTE_MULTILINE, // 多行註釋
        NOTE_MULTILINE_STAR, // 多行註釋遇到*
        NOTE_SINGLELINE, // 單行註釋
        BACKSLASH, // 折行註釋
        CODE_CHAR, // 字符
        CHAR_ESCAPE_SEQUENCE, // 字符中的轉義字符
        CODE_STRING, // 字符串
        STRING_ESCAPE_SEQUENCE// 字符串中的轉義字符
    };

    /** * @function 刪除代碼中的註釋,以String形式返回 * @param strToHandle 待刪除註釋的代碼 * @return 已刪除註釋的代碼,String字符串形式 */
    public static String delete_C_Cplusplus_Java_Note(String strToHandle) {
        StringBuilder builder = new StringBuilder();

        State state = State.CODE;// Initiate
        for (int i = 0; i < strToHandle.length(); ++i) {
            char c = strToHandle.charAt(i);
            switch (state) {
            case CODE:
                if (c == '/') {
                    state = State.SLASH;
                }else {
                    builder.append(c);
                    if(c=='\'') {
                        state=State.CODE_CHAR;
                    }else if(c=='\"') {
                        state=State.CODE_STRING;
                    }
                }                    
                break;
            case SLASH:
                if (c == '*') {
                    state = State.NOTE_MULTILINE;
                } else if (c == '/') {
                    state = State.NOTE_SINGLELINE;
                } else {
                    builder.append('/');
                    builder.append(c);
                    state = State.CODE;
                }
                break;
            case NOTE_MULTILINE:
                if(c=='*') {
                    state=State.NOTE_MULTILINE_STAR;
                }else {
                    if(c=='\n') {
                        builder.append("\r\n");//保留空行,固然,也能夠去掉
                    }
                    state=State.NOTE_MULTILINE;//保持當前狀態
                }
                break;
            case NOTE_MULTILINE_STAR:
                if(c=='/') {
                    state=State.CODE;
                }else if(c=='*') {
                    state=State.NOTE_MULTILINE_STAR;//保持當前狀態
                }
                else {
                    state=State.NOTE_MULTILINE;
                }
                break;
            case NOTE_SINGLELINE:
                if(c=='\\') {
                    state=State.BACKSLASH;
                }else if(c=='\n'){
                    builder.append("\r\n");
                    state=State.CODE;
                }else {
                    state=State.NOTE_SINGLELINE;//保持當前狀態
                }
                break;
            case BACKSLASH:
                if(c=='\\' || c=='\r'||c=='\n') {//windows系統換行符爲\r\n
                    if(c=='\n') {
                        builder.append("\r\n");//保留空行,固然,也能夠去掉
                    }
                    state=State.BACKSLASH;//保持當前狀態
                }else {
                    state=State.NOTE_SINGLELINE;
                }
                break;
            case CODE_CHAR:
                builder.append(c);
                if(c=='\\') {
                    state=State.CHAR_ESCAPE_SEQUENCE;
                }else if(c=='\'') {                    
                    state=State.CODE;
                }else {
                    state=State.CODE_CHAR;//保持當前狀態
                }
                break;
            case CHAR_ESCAPE_SEQUENCE:
                builder.append(c);
                state=State.CODE_CHAR;
                break;
            case CODE_STRING:
                builder.append(c);
                if(c=='\\') {
                    state=State.STRING_ESCAPE_SEQUENCE;
                }else if(c=='\"') {                    
                    state=State.CODE;
                }else {
                    state=State.CODE_STRING;//保持當前狀態
                }
                break;
            case STRING_ESCAPE_SEQUENCE:
                builder.append(c);
                state=State.CODE_STRING;
                break;
            default:
                break;
            }
        }
        return builder.toString();
    }

    /** * @function 從指定文件中讀取代碼內容,以String形式返回 * @param inputFileName 待刪除註釋的文件 * @return 待刪除註釋的文件中的代碼內容,String字符串形式 * @note 輸入文件格式默認爲 UTF-8 */
    public static String readFile(String inputFileName) {
        StringBuilder builder = new StringBuilder();
        try {
            FileInputStream fis = new FileInputStream(inputFileName);
            InputStreamReader dis = new InputStreamReader(fis);
            BufferedReader reader = new BufferedReader(dis);
            String s;
            // 每次讀取一行,當改行爲空時結束
            while ((s = reader.readLine()) != null) {
                builder.append(s);
                builder.append("\r\n");// windows系統換行符
            }
            reader.close();
            dis.close();
            fis.close();
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(1);
        }
        return builder.toString();
    }

    /** * @function 將刪除註釋後的代碼保存到指定新文件 * @param outputFileName 保存「刪除註釋後的代碼」的文件的文件名 * @param strHandled 刪除註釋後的代碼 */
    public static void writeFile(String outputFileName, String strHandled) {
        try {
            FileOutputStream fos = new FileOutputStream(outputFileName);
            OutputStreamWriter dos = new OutputStreamWriter(fos);
            BufferedWriter writer = new BufferedWriter(dos);
            writer.write(strHandled);
            writer.close();
            dos.close();
            fos.close();
            System.out.println("code that without note has been saved successfully in " + outputFileName);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /** * @function 讀取待處理文件,刪除註釋,處理過的代碼寫入新文件 * @param args */
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        //待刪除註釋的文件
        System.out.println("The fileName that will be delete note:");
        String inputFileName = in.nextLine();
        //保存「刪除註釋後的代碼」的文件
        System.out.println("The fileName that will save code without note:");
        String outputFileName = in.nextLine();

        String strToHandle = readFile(inputFileName);
        String strHandled = delete_C_Cplusplus_Java_Note(strToHandle);
        writeFile(outputFileName, strHandled);

    }

}複製代碼

說明

  • 本程序保留註釋佔用行,也就是說,註釋之外的代碼原樣保留(行數也不會變),註釋行變爲空白。
  • 不檢測文件後綴(這就意味着把代碼寫在.txt裏面也能夠處理),有需求的能夠自行添加。
  • 本程序適用於windows平臺,其餘平臺如linux和mac請替換「\r\n」換行符。文件格式默認爲UTF。
  • 有興趣的能夠封裝成圖形界面,直接拖入文件處理,更好用。
  • 本程序通過大量測試未發現bug,若讀者發現bug,歡迎提出。

參考

相關文章
相關標籤/搜索