實作一個過濾器程式,讀取 C 原始檔,將所有 /* ... */ 區塊註解轉換為 // 行註解,支援巢狀、混合、空註解等邊界情況。
輸入 (input.c): /*int i = 0;*/ /*int y = 0;*/int j = 0; /*int x = 0;/*12345678*/ /* int h = 0; int g = 0; int j = 0; */int q = 0; /**//**/ /* ****** */ // /*1234567890*/ 輸出 (output.c): //int i = 0; //int y = 0; int j = 0; //int x = 0;/*12345678 // //int h = 0; //int g = 0; //int j = 0// int q = 0; // // ****** // /*1234567890*/
核心採用有限狀態自動機 (FSM),兩種主要狀態:
TAG_END (0):不在區塊註解中TAG_BEGIN (1):在區塊註解內部#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <errno.h>
typedef enum { FILE_ERROR, FILE_SUCCESS, OTHER_ERROR, NO_MATCH } Status;
typedef enum { TAG_BEGIN, TAG_END } TagState;
Status convert_comments(FILE *in, FILE *out) {
int ch1, ch2;
TagState state = TAG_END;
while ((ch1 = fgetc(in)) != EOF) {
switch (ch1) {
case '/':
ch2 = fgetc(in);
if (ch2 == '*' && state == TAG_END) {
// 開始區塊註解 -> 轉為行註解
fputc('/', out); fputc('/', out);
state = TAG_BEGIN;
} else if (ch2 == '/') {
// 已是行註解,原樣複製
fputc('/', out); fputc('/', out);
while ((ch2 = fgetc(in)) != EOF && ch2 != '\n')
fputc(ch2, out);
state = TAG_END;
} else {
fputc('/', out);
if (ch2 != EOF) fputc(ch2, out);
}
break;
case '*':
ch2 = fgetc(in);
if (ch2 == '/' && state == TAG_BEGIN) {
// 結束區塊註解
fputc(' ', out); // 保留空格避免語法錯誤
state = TAG_END;
} else if (ch2 == '*') {
// 註解內的 * 字符
fputc('*', out);
fseek(in, -1, SEEK_CUR); // 退回一字符
} else {
fputc('*', out);
if (ch2 != EOF) fputc(ch2, out);
}
break;
case '\n':
if (state == TAG_BEGIN) {
// 區塊註解跨行 -> 每行加 //
fputc('\n', out);
fputc('/', out); fputc('/', out);
} else {
fputc('\n', out);
}
break;
default:
fputc(ch1, out);
}
}
return (state == TAG_END) ? FILE_SUCCESS : NO_MATCH;
}
int main(int argc, char *argv[]) {
if (argc != 3) {
fprintf(stderr, "用法: %s <輸入檔> <輸出檔>\n", argv[0]);
return 1;
}
FILE *in = fopen(argv[1], "r");
FILE *out = fopen(argv[2], "w");
if (!in || !out) {
perror("檔案開啟失敗");
return 1;
}
Status result = convert_comments(in, out);
fclose(in); fclose(out);
switch (result) {
case FILE_SUCCESS: puts("轉換成功"); break;
case FILE_ERROR: fprintf(stderr, "檔案錯誤: %d\n", errno); break;
case NO_MATCH: puts("註解不匹配(未閉合)"); break;
default: puts("其他錯誤");
}
return 0;
}
| 當前狀態 | 輸入字符 | 動作 | 下一狀態 |
|---|---|---|---|
| TAG_END | / * | 輸出 // | TAG_BEGIN |
| TAG_BEGIN | * / | 輸出空格 | TAG_END |
| TAG_BEGIN | \n | 輸出 \n // | TAG_BEGIN |
| TAG_END | / / | 複製整行 | TAG_END |
| 任意 | 其他 | 原樣輸出 | 不變 |
/**/ → //\n///* 視為普通字符"/* not comment */" 不應轉換(本實作未處理,需加入字串狀態)\ 結尾的註解行需特殊處理// 內的 /*)