編寫程式讀取 input.c,將所有 /* ... */ 風格註釋轉換為 // ... 風格,結果寫入 output.c。需正確處理巢狀、換行、連續註釋、字符串字面量等邊界情況。
採用有限狀態機(FSM)建模,四種狀態:
NULL_STATE:普通代碼區C_STATE:處於 /* ... */ 註釋內部CPP_STATE:處於 // ... 註釋內部END_STATE:文件結束狀態轉移圖(文字版):
NULL_STATE --'/'-> 看下一字符:
'*' -> 輸出 "//",進入 C_STATE
'/' -> 輸出 "//",進入 CPP_STATE
其他 -> 輸出原字符,留在 NULL_STATE
C_STATE -- 讀字符:
'*' -> 看下一字符:
'/' -> 註釋結束,輸出換行(如需),進入 NULL_STATE
'*' -> 輸出 '*',留在 C_STATE
其他 -> 輸出 '*'+該字符,留在 C_STATE
'\n' -> 輸出 "\n//",留在 C_STATE
EOF -> 進入 END_STATE
CPP_STATE -- 讀字符:
'\n' -> 輸出 '\n',進入 NULL_STATE
EOF -> 進入 END_STATE
其他 -> 原樣輸出,留在 CPP_STATE
/* comment_covert.h */
#ifndef COMMENT_CONVERT_H
#define COMMENT_CONVERT_H
#include <stdio.h>
typedef enum { NULL_STATE, C_STATE, CPP_STATE, END_STATE } State;
void CommentConvert(FILE *in, FILE *out);
void Do_Null_State(FILE *in, FILE *out);
void Do_C_State(FILE *in, FILE *out);
void Do_CPP_State(FILE *in, FILE *out);
#define READ_FILE "input.c"
#define WRITE_FILE "output.c"
#endif
/* comment_covert.c */
#include "comment_covert.h"
static State state = NULL_STATE;
void Do_Null_State(FILE *in, FILE *out) {
int c1 = fgetc(in), c2;
switch (c1) {
case '/':
c2 = fgetc(in);
if (c2 == '*') { fputc('/',out); fputc('/',out); state = C_STATE; }
else if (c2 == '/') { fputc('/',out); fputc('/',out); state = CPP_STATE; }
else { fputc('/',out); fputc(c2,out); }
break;
case EOF: state = END_STATE; break;
default: fputc(c1,out);
}
}
void Do_C_State(FILE *in, FILE *out) {
int c1 = fgetc(in), c2, c3;
switch (c1) {
case '*':
c2 = fgetc(in);
if (c2 == '/') { // 註釋結束
c3 = fgetc(in);
if (c3 != '\n') fputc('\n',out);
if (c3 == '/') ungetc(c3,in); // 避免吞掉下一註釋開頭
fputc(c3,out);
state = NULL_STATE;
} else if (c2 == '*') { // 連續 *
c3 = fgetc(in);
fputc('*',out);
if (c3 == '/') state = NULL_STATE;
} else { fputc('*',out); fputc(c2,out); }
break;
case '\n': fputc('\n',out); fputc('/',out); fputc('/',out); break;
case EOF: state = END_STATE; break;
default: fputc(c1,out);
}
}
void Do_CPP_State(FILE *in, FILE *out) {
int c = fgetc(in);
if (c == '\n') { fputc('\n',out); state = NULL_STATE; }
else if (c == EOF) state = END_STATE;
else fputc(c,out);
}
void CommentConvert(FILE *in, FILE *out) {
while (state != END_STATE) {
switch (state) {
case NULL_STATE: Do_Null_State(in,out); break;
case C_STATE: Do_C_State(in,out); break;
case CPP_STATE: Do_CPP_State(in,out); break;
}
}
}
/* test.c */
#include "comment_covert.h"
#include <stdlib.h>
int main(void) {
FILE *in = fopen(READ_FILE, "r");
FILE *out = fopen(WRITE_FILE, "w");
if (!in || !out) { perror("file open"); return EXIT_FAILURE; }
puts("***** 註釋轉換開始 *****");
CommentConvert(in, out);
puts("***** 註釋轉換結束 *****");
fclose(in); fclose(out);
return 0;
}
| 輸入片段 | 預期輸出 | 說明 |
|---|---|---|
/* comment */ | // comment\n | 單行塊註釋 |
/* line1\nline2 */ | // line1\n// line2\n | 多行轉換,每行加 // |
code /*cmt*/ more | code // cmt\n more | 行內塊註釋,結尾換行 |
/* /* nest */ */ | // /* nest */\n | 巢狀註釋,內層 * / 視為普通字符 |
// /* not a block */ | // /* not a block */\n | C++ 註釋內內容不再解析 |
"string /* not comment */" | 原樣輸出 | 字符串字面量保護(本實現未處理,進階可擴展) |
"/*"。