【问题标题】:Unexpected behaviour when removing inline comments in C在 C 中删除内联注释时出现意外行为
【发布时间】:2023-03-27 21:24:01
【问题描述】:

堆栈溢出!我正在学习 C 技术。我有一个函数,它获取一个输入文件,在文件中查找并将内容写入没有 cmets 的输出文件。 该功能有效,但在某些情况下也会刹车。 我的功能:

    void removeComments(char* input, char* output)
{
    FILE* in = fopen(input,"r");
    FILE* out = fopen(ouput,"w");
    char c;
    while((c = fgetc(in)) != EOF)
    {
       if(c == '/')
       {

          c = fgetc(in);
          if(c == '/')
          {
           while((c = fgetc(in)) != '\n');
          }
          else
          {
            fputc('/', out);
          }
       }
       else
       {
           fputc(c,out);
       }
    }
    fclose(in);
    fclose(out);
}

但是当我将这样的文件作为输入时:

// Parameters: a, the first integer; b the second integer.
// Returns: the sum.
int add(int a, int b) 
{
    return a + b; // An inline comment.
}
int sample = sample;

当删除内联注释时,由于某种原因它无法到达“\n”并给出输出:

int add(int a, int b) 
{
    return a + b; }
int sample = sample;

[编辑] 谢谢你帮助我!它适用于我发布的案例,但它在另一个案例中刹车。 当前代码:

FILE* in = fopen(input,"r");
FILE* out = fopen(output,"w");

if (in == NULL) {
  printf("cannot read %s\n", input);
  return; /* change signature to return 0 ? */
}
if (out == NULL) {
  printf("cannot write in %s\n", output);
  return; /* change signature to return 0 ? */
}

int c;
int startline = 1;

while((c = fgetc(in)) != EOF)
{
   if(c == '/')
   {
      c = fgetc(in);

      if(c == '/')
      {
        while((c = fgetc(in)) != '\n')
        {
          if (c == EOF) {
            fclose(in);
            fclose(out);
            return; /* change signature to return 1 ? */
          }
        }
        if (! startline)
          fputc('\n', out);
        startline = 1;
      }
      else if (c == EOF)
        break;
      else {
        fputc('/', out);
        startline = 0;
      }
   }
   else
   {
     fputc(c,out);
     startline = (c == '\n');
   }
}

fclose(in);
fclose(out);

当文件包含除法时,第二个变量消失。 示例:

int divide(int a, int b) 
    {
        return a/b; 
    }

它回馈:

int divide(int a, int b) 
    {
        return a/; 
    }

【问题讨论】:

  • 你的代码有几个问题,如果你想看我的回答
  • 除了 Bruno 的 cmets,想想如果序列“//”出现在字符串文字中会发生什么(这里不是注释)
  • @DavidC。是的,我也在考虑;-)
  • 另外两个讨厌的东西:// this comment continues \(在换行符之前加上反斜杠)意味着一行注释至少继续到下一行。注释开头的类似结构 — 包含 "/\\\n/ This is a comment\n" 的字符串,其中第一个和第二个斜杠之间有一个反斜杠换行符序列(或多个 BSNL 序列)也标志着评论的开始。诚然,编写此类代码的程序员应该被枪杀、绞死、画图和四等分,但有些程序需要防弹。您是否担心它的呼吁。
  • @bruno 谢谢。谢谢大家!

标签: c comments c-preprocessor


【解决方案1】:

之后

while((c = fgetc(in)) != '\n');

你需要一个fputc('\n', out);

补充说明:

char c;
while((c = fgetc(in)) != EOF)

c 必须是一个 int 才能管理 EOF

只是一个错字:输出必须是输出才能编译

读完'/'后你没有很好地管理EOF

你错过了检查fopen的结果


提案:

#include <stdio.h>

void removeComments(char* input, char* output)
{
    FILE* in = fopen(input,"r");
    FILE* out = fopen(output,"w");

    if (in == NULL) {
      printf("cannot read %s\n", input);
      return; /* change signature to return 0 ? */
    }
    if (out == NULL) {
      printf("cannot write in %s\n", output);
      return; /* change signature to return 0 ? */
    }

    int c;

    while((c = fgetc(in)) != EOF)
    {
       if(c == '/')
       {
          c = fgetc(in);

          if(c == '/')
          {
            while((c = fgetc(in)) != '\n')
            {
              if (c == EOF) {
                fclose(in);
                fclose(out);
                return; /* change signature to return 1 ? */
              }
            }
            fputc('\n', out);
          }
          else if (c == EOF) {
            fputc('/', out);
            break;
          }
          else
            fputc('/', out);
            fputc(c, out);
       }
       else
       {
         fputc(c,out);
       }
    }

    fclose(in);
    fclose(out);
     /* change signature to return 1 ? */
}

int main(int argc, char ** argv)
{
  removeComments(argv[1], argv[2]);
}

正如 Tormund Giantsbane 在评论中所说,最好完全删除仅包含评论的行(评论从第一列开始),新提案就是这样做的:

#include <stdio.h>

void removeComments(char* input, char* output)
{
    FILE* in = fopen(input,"r");
    FILE* out = fopen(output,"w");

    if (in == NULL) {
      printf("cannot read %s\n", input);
      return; /* change signature to return 0 ? */
    }
    if (out == NULL) {
      printf("cannot write in %s\n", output);
      return; /* change signature to return 0 ? */
    }

    int c;
    int startline = 1;

    while((c = fgetc(in)) != EOF)
    {
       if(c == '/')
       {
          c = fgetc(in);

          if(c == '/')
          {
            while((c = fgetc(in)) != '\n')
            {
              if (c == EOF) {
                fclose(in);
                fclose(out);
                return; /* change signature to return 1 ? */
              }
            }
            if (! startline)
              fputc('\n', out);
            startline = 1;
          }
          else if (c == EOF) {
            fputc('/', out);
            break;
          }
          else {
            fputc('/', out);
            fputc(c, out);
            startline = 0;
          }
       }
       else
       {
         fputc(c,out);
         startline = (c == '\n');
       }
    }

    fclose(in);
    fclose(out);
     /* change signature to return 1 ? */
}

int main(int argc, char ** argv)
{
  removeComments(argv[1], argv[2]);
}

编译和执行:

pi@raspberrypi:/tmp $ gcc -pedantic -Wextra -g r.c
pi@raspberrypi:/tmp $ cat i
// Parameters: a, the first integer; b the second integer.
// Returns: the sum.
int add(int a, int b) 
{
    return a + b/c; // An inline comment.
}
int sample = sample;
pi@raspberrypi:/tmp $ ./a.out i o
pi@raspberrypi:/tmp $ cat o
int add(int a, int b) 
{
    return a + b/c; 
}
int sample = sample;

正如 DavidC 所说。在备注中,如果 // 放在字符串中,结果将不是预期的结果,即使是非法字符也是如此(我的意思是 '//' 不能更改),C cmets (/ * .. // ... */) 等

【讨论】:

    【解决方案2】:

    当删除内联注释时,由于某种原因无法到达“\n”

    不,如果它未能到达或看到内联注释末尾的换行符,那么程序可能会消耗整个文件的其余部分。它实际上没有做的是 write 这样的换行符到输出中。

    考虑一下你的吃评论代码:

               while((c = fgetc(in)) != '\n');
    

    当读取换行符时,该循环终止。此时,已经读取的换行符无法再次从输入中读取,因此您的一般读/写规定将无法处理它。如果您希望保留此类换行符,则需要在注释处理分支中打印它们。

    补充说明:

    1. fgetc 返回一个int,而不是char,您需要这样处理它才能正确检测到文件结尾。

    2. 如果输入以未由换行符终止的内联注释结束,您的程序将进入无限循环。这样的来源在技术上是不合格的,但即便如此,你也应该处理它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-13
      • 1970-01-01
      相关资源
      最近更新 更多