1

我有一个文本文件,最多包含 100 个 IP 地址,每行 1 个。我需要将每个地址作为字符串读入一个名为“list”的数组中。首先,我假设“列表”需要是一个二维字符数组。每个 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");  
  }

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

编辑:

我用这个替换了 fgets 代码:

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

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

4

7 回答 7

6

首先,阅读:

      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 个用于换行符,1 个用于 NUL 终止符),否则您可能希望将数据读入临时缓冲区,然后将其复制到您list的你已经剥离了新线。

在您当前用于打印字符串的代码中,您一次处理一个字符,这可以工作,但不必要地困难。有几个人建议使用 %s printf 转换,这本身就很好。但是,要使用它,您必须稍微简化索引。打印前六个地址如下所示:

for (i=0; i<6; i++)
    printf("%s", list[i]);
于 2009-12-03T19:50:20.410 回答
4

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

想想那些循环:在 i=0 和 j=0 的情况下,它最多可以读取 11 个字符&list[0][0]。然后在 i=0 和 j=1 的情况下,它将另外 11 个字符读取到&list[0][1]. 这是错误的,原因有两个 - 它覆盖了最后一次调用的结果,并且可能写入的字节数超过了 list[0] 可以容纳的字节数。

于 2009-12-03T19:46:12.743 回答
1

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

您可能会在第一次调用 fgets 时读取前 12 个字符,然后第二次调用将捕获换行符,然后第三次调用获取下一行。

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

于 2009-12-03T19:50:50.863 回答
1

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

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]);
}
于 2009-12-03T19:51:22.130 回答
1

对于 (i = 0; i < 100; i++) {

   if (feof(fp))
       break;

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

}

于 2009-12-03T19:52:27.447 回答
1

我写了一个读取行的函数。我觉得应该是安全的。

检查:io_readline

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

于 2009-12-03T20:41:39.270 回答
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 关于数组和指针的惯用伎俩……

当数组表达式出现在大多数上下文中时,表达式的类型会隐式地从“T 的 N 元素数组”转换为“指向 T 的指针”,表达式的值是数组第一个元素的地址。此规则的例外情况是数组表达式是sizeofor&运算符的操作数,或者它是在声明中用作初始值设定项的字符串文字。当你听到人们说“数组和指针是一回事”时,他们就是在混淆这个规则。数组和指针是完全不同的动物,但在某些情况下它们可以互换使用。

请注意,在上面的代码中,我list[i]作为第一个参数传递给 fgets() 没有任何修饰(例如&运算符)。即使 的类型list[i]是“char 的 12 元素数组”,在这种情况下,它也会隐式转换为“指向 char 的指针”类型,并且值将是 的地址list[i][0]。请注意,我还将相同的表达式传递给sizeof操作员。在这种情况下,数组表达式的类型不会转换为指针类型,并且 sizeof 运算符会返回数组类型中的字节数 (12)。

只是为了确定:

表达式类型隐式转换为
---------- ---- ----
list char [100][12] char (*)[12](指向 char 的 12 元素数组的指针)
列表 [i] 字符 [12] 字符 *
list[i][j] 字符 N/A

这意味着fgets()它将读取最多接下来的 12 个字符(前提是它没有首先遇到换行符或 EOF)并将其存储在list[i][0]. 请注意,这fgets()将在字符串末尾写入一个终止 nul 字符 (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;
  }
}
于 2009-12-03T20:46:03.467 回答