不要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 的指针”,表达式的值是数组第一个元素的地址。此规则的例外情况是数组表达式是sizeof
or&
运算符的操作数,或者它是在声明中用作初始值设定项的字符串文字。当你听到人们说“数组和指针是一回事”时,他们就是在混淆这个规则。数组和指针是完全不同的动物,但在某些情况下它们可以互换使用。
请注意,在上面的代码中,我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;
}
}