从C文件中删除注解

v1l68za4  于 11个月前  发布在  其他
关注(0)|答案(2)|浏览(93)

**工作目的:**学习如何使用标准C库的函数处理文件
**任务:**有一个C程序的文件,需要删除其中的所有注解,将没有注解的代码写到一个新文件中。
解释和实现特性:

1.程序文件可能非常大,因此,不能事先将整个文件读入数组。
1.注解可以是单行或多行。如果使用“反斜杠”字符-“"将单行注解移到下一行,则单行注解也可以由多行组成。
1.在C语言中没有嵌套注解
1.不考虑字符串常量内的注解
1.一个文件不一定代表一个正确的C程序。例如,一个注解可能会在没有关闭的情况下断开
1.允许出现一些新的空格和/或换行符来代替已删除的注解,也允许缺少一些现有的非重要分隔符字符。
1.不允许从用引号(双引号和单引号)标记的常量字符串中删除数据。

**输入和输出数据:**源文件始终命名为test. c,输出文件必须命名为test.wc
我的代码:

#include <stdio.h>

#define TRUE 1
#define FALSE 0

typedef int BOOL;

int mygetc (FILE *in) {
        for (;;) {
                int c = getc(in);
                if (c == '\\') {
                        c = getc(in);
                        if (c == '\n')
                                continue;
                        if (c != EOF)
                                ungetc(c, in);
                        c = '\\';
                }
                return c;
        }
}

int skip_line_comment (FILE *in) {
        int c;
        while ((c = mygetc(in)) != '\n' && c != EOF)
                continue;
        return c;
}

int skip_block_comment (FILE *in) {
        int c;
        for (;;) {
                while ((c = mygetc(in)) != '*') {
                        if (c == EOF)
                                return c;
                }
                while ((c = mygetc(in)) == '*')
                        continue;
                if (c == EOF)
                        return c;
                if (c == '/')
                        return ' ';
        }
}

void removeComments (FILE *in, FILE *out) {
        int c;
        while ((c = mygetc(in)) != EOF) {
                if (c == '"' || c == '\'') {
                        int separator = c;
                        fputc(c, out);
                        while ((c = mygetc(in)) != separator && c != EOF) {
                                fputc(c, out);
                                if (c == '\\') {
                                        c = mygetc(in);
                                        if (c == EOF) break;
                                        fputc(c, out);
                                }
                        }
                } else if (c == '/') {
                        c = mygetc(in);
                        if (c == '/') c = skip_line_comment(in);
                        else if (c == '*') c = skip_block_comment(in);
                        else fputc('/', out);
                        
                }
                if (c == EOF) break;
                fputc(c, out);
        }
}

int main () {
        const char inName[20]  = "test.c";
        const char outName[20] = "test.wc";
        FILE *in;
        FILE *out;
  
        in  = fopen(inName, "r");
        out = fopen(outName, "w");
       
        removeComments(in, out);

        fclose(in);
        fclose(out);
  
        return 0;
}

字符串

.zip,包含测试:Google Disk

1.测试1 -正确
1.测试2 -正确
1.测试3 -第一行中缺少“/”
1.测试4 -在一行中预期“*”,但在不同的行中得到 *
1.测试5 -正确
1.测试6 -““出现问题,得到一些必须删除的额外行,如“"\”
1.测试7 -正确
1.测试8 -剥离多余的管路
1.测试9、10 -组合/和\的问题
1.测试11 -正确
1.测试12 -正确
1.测试13 -正确

dxpyg8gm

dxpyg8gm1#

这个问题涉及多个问题:

  • 不清楚输出程序是否会以细微的方式与源程序不同,
  • 行结束(LF或CR/LF对)
  • 不重要的起始和/或结尾空格
  • 空行
  • 隐藏的转义换行符序列(\后跟和行尾序列)
  • 测试文件使用DOS行尾序列(CR/LF)进行编码,预期的输出文件使用unix行尾(LF)和DOS行尾序列的不一致组合。zip文件还包含macOS属性文件(在**__MACOSX/**目录中),表明它是在Mac上制作的,它使用unix行尾,不翻译以文本模式打开的文件的行尾。("r""w")。很可能预期的输出文件是在Mac或Unix系统上从DOS源文件生成的,其中DOS行尾序列是源文件中未更改的副本,但替换多行注解的'\n'输出没有被标准库转换为CR/LF序列,因为它们在遗留系统上会被转换。
  • 测试文件包含许多不一致的地方,例如缺少空格和换行符,甚至缺少最后的换行符!问题陈述应该更精确,并指定单行注解必须替换为单个换行符,多行注解必须替换为单个空格。然而,用单个空格替换多行注解并不总是合适的:在#define a/**/(b)中,这将导致a(1)被定义为扩展到(b)(1)而不是什么都没有。如果多行注解的缺失会导致下一个字符成为前一个标记的一部分,则多行注解仅应扩展为空格,这对于实现来说并不简单。
  • 测试文件中的另一个问题是在字符串文字中存在嵌入的未转义的换行符(例如:test(5).cline 8)。expected test(5).wc文件似乎期望字符串在换行符处结束。源文件是无效的,但是如果要检查任何 expected 输出,则应指定此类语法错误的行为。

似乎很难编写一个程序来精确地产生预期的输出。
您的程序非常简单,似乎是正确的,除了一些限制和错误:

  • 如果在unix系统上编译,它不能识别CR/LF序列,所以它不能将\后跟CR/LF作为转义换行符处理。这会阻止正确处理///**/序列被分成两行并带有一个或多个转义换行符的注解。
  • 该程序不处理序列/"作为一个/后跟一个字符串的开始,因为"是由fputc(c, out)在主体的末尾输出的,只有下一个字符与'"进行比较。/'也有同样的问题。
  • 程序会抑制所有转义的换行符,包括那些外部注解,这似乎不是测试文件所期望的。
  • 它不处理像??/这样的三重字符,在删除转义换行符 * 之前 * 转换为\(对三重字符的支持将是迂腐的,因为这个功能已经从最新的C标准中删除了,除了在测试套件中,从来没有使用过)。
  • 它不处理在C++风格的数字分隔符中使用的单引号(现在是C23的标准)。(例如:int x = 1'000;)。这种丑陋的约定使得正确解析C标记更加困难。非常不幸的是,C23没有像几乎所有其他现代语言那样将_标准化为数字分隔符,与现有的预处理器和解析器兼容的约定X1 m22n1x已经在pp-numbers中被接受。X1 m23n1x将被解释为开始字符常量,导致程序的其余部分被错误解释,并导致注解剥离失败。

这是你的程序的修改版本,修正了行尾问题和/"/'解析错误。它仍然抑制转义的换行符,忽略三字母组,并将'误解为数字分隔符。

#include <errno.h>
#include <stdio.h>
#include <string.h>

int mygetc0(FILE *in) {
    int c = getc(in);
    if (c == '\r') {
        /*  handle legacy line end combinations */
        c = getc(in);
        if (c != '\n' && c != EOF)
            ungetc(c, in);
        c = '\n';
    }
    return c;
}

int mygetc(FILE *in) {
    for (;;) {
        int c = mygetc0(in);
        if (c == '\\') {
            c = mygetc0(in);
            if (c == '\n')
                continue;
            if (c != EOF)
                ungetc(c, in);
            c = '\\';
        }
        return c;
    }
}

int skip_line_comment(FILE *in) {
    int c;
    while ((c = mygetc(in)) != '\n' && c != EOF)
        continue;
    return c;
}

int skip_block_comment(FILE *in) {
    int c;
    for (;;) {
        while ((c = mygetc(in)) != '*') {
            if (c == EOF)
                return c;
        }
        while ((c = mygetc(in)) == '*')
            continue;
        if (c == EOF)
            return c;
        if (c == '/')
            return ' ';
    }
}

void removeComments(FILE *in, FILE *out) {
    int c;
    while ((c = mygetc(in)) != EOF) {
        if (c == '"' || c == '\'') {
            int separator = c;
            fputc(c, out);
            while ((c = mygetc(in)) != separator && c != EOF) {
                fputc(c, out);
                if (c == '\\') {
                    c = mygetc(in);
                    if (c == EOF)
                        break;
                    fputc(c, out);
                }
            }
        } else if (c == '/') {
            c = mygetc(in);
            if (c == '/') {
                c = skip_line_comment(in);
            } else
            if (c == '*') {
                c = skip_block_comment(in);
            } else {
                if (c != EOF)
                    ungetc(c, in);
                c = '/';
            }
        }
        if (c == EOF)
            break;
        fputc(c, out);
    }
}

int strip_file(const char *inName) {
    FILE *in;
    FILE *out;
    int len = strlen(inName);

    if (len >= 2 && !strcmp(inName + len - 2, ".c"))
        len -= 2;

    char outName[len + 4];
    snprintf(outName, sizeof outName, "%.*s.wc", len, inName);

    if ((in = fopen(inName, "r")) == NULL) {
        fprintf(stderr, "cannot open %s: %s\n", inName, strerror(errno));
        return 1;
    }
    if ((out = fopen(outName, "w")) == NULL) {
        fprintf(stderr, "cannot open %s: %s\n", outName, strerror(errno));
        fclose(in);
        return 1;
    }

    removeComments(in, out);

    fclose(in);
    fclose(out);
    return 0;
}

int main(int argc, char *argv[]) {
    int res = 0;
    if (argc > 1) {
        for (int i = 1; i < argc; i++) {
            res |= strip_file(argv[i]);
        }
    } else {
        res |= strip_file("test.c");
    }
    return res;
}

字符串

tmb3ates

tmb3ates2#

决定完全重写程序。现在每种情况都有一个单独的算法。它通过了所有13个测试。

#include <stdio.h>

#define BUFSIZE 2048
#define CODE                        0
#define STAR_IN_MULTI_COMMENT       1
#define SLASH_IN_LINE_COMMENT       2
#define BEGIN_COMMENT               3
#define STRING                      4
#define MULTI_COMMENT               5
#define LINE_COMMENT                6
#define SLASH_IN_STRING             7
#define LITERAL                     8
#define SLASH_IN_LITERAL            9

void processFile(FILE *filei, FILE *fileo);

int main() {
    char filenamei[] = "test.c";
    char filenameo[] = "test.wc";

    FILE *filei, *fileo;

    if ((filei = fopen(filenamei, "r")) == NULL
    || (fileo = fopen(filenameo, "w")) == NULL) {
        return 0;
    }

    processFile(filei, fileo);

    fclose(filei);
    fclose(fileo);

    return 0;
}

void processFile(FILE *filei, FILE *fileo) {
    int i, size, pOutStr, state;
    char buffer[BUFSIZE], result[BUFSIZE];

    state = CODE;
    do {
        pOutStr = 0;
        size = fread(buffer, 1, BUFSIZE, filei);

        for(i = 0; i < size; i++) {
            switch (state) {
                case CODE:
                    if (buffer[i] == '\"') {
                        state = STRING;
                        result[pOutStr++] = buffer[i];
                    } else if (buffer[i] == '/') {
                        state = BEGIN_COMMENT;
                    } else if (buffer[i] == '\'') {
                        state = LITERAL;
                        result[pOutStr++] = buffer[i];
                    } else {
                        result[pOutStr++] = buffer[i];
                    }
                    break;

                case STAR_IN_MULTI_COMMENT:
                    if (buffer[i] == '/') {
                        state = CODE;
                    } else if (buffer[i] != '*') {
                        state = MULTI_COMMENT;
                    }
                    break;

                case SLASH_IN_LINE_COMMENT:
                    state = LINE_COMMENT;
                    break;

                case BEGIN_COMMENT:
                    if (buffer[i] == '*') {
                        state = MULTI_COMMENT;
                    } else if (buffer[i] == '/') {
                        state = LINE_COMMENT;
                    } else {
                        result[pOutStr++] = '/';
                        --i;
                        state = CODE;
                    }
                    break;

                case STRING:
                    if (buffer[i] == '"') {
                        state = CODE;
                    } else if (buffer[i] == '\\') {
                        state = SLASH_IN_STRING;
                    } else if (buffer[i] == '\n') {
                        state = CODE;
                    }
                    result[pOutStr++] = buffer[i];
                    break;

                case LITERAL:
                    if (buffer[i] == '\'') {
                        state = CODE;
                    } else if (buffer[i] == '\\') {
                        state = SLASH_IN_LITERAL;
                    } else if (buffer[i] == '\n') {
                        state = CODE;
                    }
                    result[pOutStr++] = buffer[i];
                    break;

                case MULTI_COMMENT:
                    if (buffer[i] == '*') {
                        state = STAR_IN_MULTI_COMMENT;
                    }
                    break;

                case LINE_COMMENT:
                    if (buffer[i] == '\\') {
                        state = SLASH_IN_LINE_COMMENT;
                    } else if (buffer[i] == '\n') {
                        state = CODE;
                        result[pOutStr++] = buffer[i];
                    }
                    break;

                case SLASH_IN_STRING:
                    state = STRING;
                    result[pOutStr++] = buffer[i];
                    break;

                case SLASH_IN_LITERAL:
                    state = (buffer[i] != '\n') ? LITERAL : CODE;
                    result[pOutStr++] = buffer[i];
                    break;

                default:
                    break;
            }
        }

        if (pOutStr) {
            fwrite(result, 1, pOutStr, fileo);
        }

    } while(!feof(filei));
}

字符串

相关问题