首页 文章

如何在C中逐行读取文件?

提问于
浏览
1

我有一个文本文件,最多100个IP地址,每行1个 . 我需要将每个地址作为字符串读入名为“list”的数组中 . 首先,我假设“list”需要是一个二维char数组 . 每个IP地址长度为11个字符,如果包含“\ 0”则为12个,因此我声明列表如下:

char list[100][12];

接下来,我尝试使用fgets来读取流:

for (i = 0; i < 100; i++)  
  {  
      if (feof(stream))  
          break;  
          for (j = 0; j < 12; j++)  
          fgets(&list[i][j], 12, stream);  
      count++;  
  }

要检查字符串是否已正确读取,我尝试输出它们:

for (i = 0; i < 5; i++)  
  {  
      for (j = 0; j < 11; j++)  
          printf("%c", list[i][j]);  
      printf("\n");  
  }

运行程序后,很明显出错了 . 作为初学者,我不确定是什么,但我猜我正在读错文件 . 没有错误 . 它编译,但在两行打印一个奇怪的地址 .

Edit:

我用这个替换了fgets代码:

for (i = 0; i < 100; i++)
  {
      if (feof(stream))
          break;
      fgets(list[i], 12, stream);
      count++;
  }

它现在打印五个字符串,但它们是来自内存的“随机”字符 .

7 回答

  • 4

    一,阅读:

    for (j = 0; j < 12; j++)  
          fgets(&list[i][j], 12, stream);
    

    你这里有一个大问题 . 这是尝试将字符串读入数组中的每个连续字符 .

    总而言之,我认为你让它变得比它需要的复杂得多 . 将您的数组视为100个字符串, fgets 将一次使用一个字符串 . 这意味着阅读可能看起来像这样:

    for (i=0; i<100 && fgets(list[i], 11, string); i++)
        ;
    

    还有另外一个小细节需要处理: fgets() 通常会在每行末尾保留换行符 . 因此,您可能需要为13个字符留出空间(地址为11,新行为1,NUL终结符为1),否则您可能希望将数据读入临时缓冲区,并将其复制到 list 在你剥离了新线之后 .

    在您当前打印字符串的代码中,您一次只能处理一个字符,这可能会起作用,但这是不必要的困难 . 有几个人建议使用%s printf转换,这本身就很好 . 但是,要使用它,您必须简化索引 . 打印前六个地址看起来像这样:

    for (i=0; i<6; i++)
        printf("%s", list[i]);
    
  • 1

    您对 fgets 的调用最多可从流中读取11个字符到数组中 . 所以你不想为每个字符串的每个字符调用一次 .

    想想那些循环:当i = 0且j = 0时,它最多可读取11个字符到 &list[0][0] . 然后在i = 0且j = 1的情况下,它将另外11个字符读取到 &list[0][1] . 这有两个原因 - 它覆盖了最后一次调用的结果,并且可能写入比list [0]更多的字节 .

  • 1

    换行符使fgets停止读取,但它被认为是有效字符,因此它包含在复制到str的字符串中 .

    您可能正在读取第一个调用fgets的前12个字符,然后第二个调用将捕获换行符,然后第三个调用将获取下一行 .

    尝试使用15个字符限制的fgets,并扩展缓冲区 .

  • 6

    第二个循环不是必需的,它会破坏你的记忆 . 你应该这样做,

    for (i = 0; i < 100; i++)
    {
    if (feof(stream))
    break;
    fgets(&list[i][j], 12, stream);
    count++;
    }
    
    To check to see if the strings were read properly, I attempt to output them:
    
    for (i = 0; i < 5; i++)
    {
    printf("%s\n", list[i]);
    }
    
  • 1

    for(i = 0; i <100; i){

    if (feof(fp))
           break;
    
       fscanf(fp,"%s\n",list[i]);
    

    }

  • 1

    不要使用 feof() 作为循环条件;在你尝试读取文件末尾之后它才会返回true,这意味着你的循环执行时间过多 . 检查输入调用的结果(无论是使用 fgets() 还是 fscanf() )查看是否成功,如果出现错误,请检查 feof() .

    if (fgets(buffer, sizeof buffer, stream) != NULL)
    {
      // process the input buffer
    }
    else if (feof(stream)
    {
      // handle end of file
    }
    else
    {
      // handle read error other than EOF
    }
    

    fgets() 读取整个字符串,而不是单个字符,因此您不希望传递字符串中每个字符的地址 . 相反称它为:

    if (fgets(list[i], sizeof list[i], stream) != NULL)
    {
      // process input address
    }
    

    而现在,对于Bode关于数组和指针的常见内容......

    当数组表达式出现在大多数上下文中时,表达式的类型将从"N-element array of T"隐式转换为"pointer to T",表达式的值是数组的第一个元素的地址 . 此规则的例外情况是数组表达式是 sizeof& 运算符的操作数,或者它是在声明中用作初始值设定项的字符串文字 . 当你听到人们说"arrays and pointers are the same thing"时,他们正在嘲笑这条规则 . 数组和指针是完全不同的动物,但在某些情况下它们可以互换使用 .

    请注意,在上面的代码中,我传递 list[i] 作为fgets()的第一个参数,没有任何装饰(例如 & 运算符) . 即使 list[i] 的类型是"12-element array of char",在此上下文中它也会隐式转换为"pointer to char"类型,并且值将为地址 list[i][0] . 请注意,我也将相同的表达式传递给 sizeof 运算符 . 在这种情况下,数组表达式的类型不会转换为指针类型,而sizeof运算符将返回数组类型中的字节数(12) .

    只是为了解决它:

    Expression      Type             Implicitly converted to
    ----------      ----             ----
    list            char [100][12]   char (*)[12] (pointer to 12-element array of char)
    list[i]         char [12]        char *
    list[i][j]      char             N/A
    

    所有这一切意味着 fgets() 将读取接下来的12个字符(前提是它没有首先触及换行符或EOF)并从 list[i][0] 开始存储它 . 请注意 fgets() 将在字符串末尾写入终止空字符(0) . 另请注意,如果 fgets() 遇到换行符并且目标数组中有空间并且终止nul, fgets() 将在nul字符之前存储终止换行符 . 所以如果你的输入文件有一行像

    1.1.1.1\n
    

    那么读取后输入缓冲区的内容将是 "1.1.1.1\n\0xxx" ,其中 x 是一些随机值 . 如果你不想在那里使用换行符,可以使用 strchr() 函数找到它,然后用0覆盖它:

    char *newline;
    ...
    if ((newline = strchr(input[i], '\n')) != NULL)
    {
      *newline = 0;
    }
    

    由于 fgets() 在下一个换行符处停止,并且由于输入缓冲区的大小为12个字符,因此您可能会遇到一个情况,即您有一个换行符作为文件中的下一个输入字符;在这种情况下, fgets() 将只将该换行符写入输入缓冲区,因此您将有一些空条目,这可能不是您想要的 . 您可能希望在输入缓冲区中添加一个额外的字节,以避免出现这种情况 .

    把它们放在一起:

    char list[100][13];
    ...
    for (i = 0; i < 100; ++)
    {
      if (fgets(list[i], sizeof list[i], stream) != NULL)
      {
        char *newline = strchr(list[i], '\n');
        if (newline != NULL)
          *newline = 0;
        printf("Read address \"%s\"\n", list[i]);
        count++;
      }
      else if (feof(stream))
      {
        printf("Reached end of file\n");
        break;
      }
      else
      {
        printf("Read error on input; aborting read loop\n");
        break;
      }
    }
    
  • 1

    我写了一个阅读线条的功能 . 我认为它应该是安全的 .

    检查:io_readline

    https://github.com/arhuaco/junkcode/blob/master/junk/misc/atail.c

相关问题