我的文本文件最多包含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");
}
运行程序后,很明显出错了。作为初学者,我不确定是什么,但我猜我正在读错文件。没有错误。它编译,但在两行打印一个奇怪的地址。
修改
我用这个替换了fgets代码:
for (i = 0; i < 100; i++)
{
if (feof(stream))
break;
fgets(list[i], 12, stream);
count++;
}
它现在打印五个字符串,但它们是来自内存的“随机”字符。
答案 0 :(得分: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终结符),否则您可能希望将数据读入临时缓冲区,并将其复制到{{ 1}}只有在你剥离了新线之后。
在您当前打印字符串的代码中,您一次只能处理一个字符,这可能会起作用,但却不必要。有几个人建议使用%s printf转换,这本身就很好。但是,要使用它,您必须简化索引。打印前六个地址看起来像这样:
list
答案 1 :(得分:4)
您对fgets
的调用会从流中读取最多11个字符到阵列中。因此,您不希望为每个字符串的每个字符调用一次。
考虑一下这些循环:i = 0且j = 0,它最多可读取11个字符到&list[0][0]
。然后,当i = 0且j = 1时,它将另外11个字符读取到&list[0][1]
。这有两个原因 - 它覆盖了最后一次调用的结果,并且可能写入比list [0]更多的字节。
答案 2 :(得分:1)
换行符使fgets停止读取,但它被认为是有效字符,因此它包含在复制到str的字符串中。
您可能正在读取第一个调用fgets的前12个字符,然后第二个调用将捕获换行符,然后第三个调用将获取下一行。
尝试使用15个字符限制的fgets,并扩展缓冲区。
答案 3 :(得分: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]);
}
答案 4 :(得分:1)
for(i = 0; i <100; i ++) {
if (feof(fp))
break;
fscanf(fp,"%s\n",list[i]);
}
答案 5 :(得分:1)
我写了一个阅读线条的功能。我认为它应该是安全的。
检查:io_readline
https://github.com/arhuaco/junkcode/blob/master/junk/misc/atail.c
答案 6 :(得分: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的指针”,表达式的值是第一个元素的地址。数组。此规则的例外情况是,数组表达式是sizeof
或&
运算符的操作数,或者它是一个字符串文字,用作声明中的初始值设定项。当你听到人们说“阵列和指针是同一个东西”时,他们就会嘲笑这个规则。数组和指针是完全不同的动物,但在某些情况下它们可以互换使用。
请注意,在上面的代码中,我将list[i]
作为fgets()的第一个参数传递而没有任何修饰(例如&
运算符)。即使list[i]
的类型是“12个元素的char数组”,在此上下文中它也会隐式转换为类型“指向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()
会在字符串末尾写入一个终止的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;
}
}