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

我有一个文本文件,最多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++; } 

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

一,阅读:

  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]); 

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

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

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

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

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

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

 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]); } 

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

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

}

不要使用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的元素数组T”转换为“指向T的指针”,并且表达式的值是数组的第一个元素的地址。 此规则的例外情况是数组表达式是sizeof&运算符的操作数,或者它是一个字符串文字,用作声明中的初始值设定项。 当你听到人们说“arrays和指针是同一个东西”时,他们就会嘲笑这个规则。 数组和指针是完全不同的动物,但在某些情况下它们可以互换使用。

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

只是为了解决它:

表达式类型隐式转换为
 ---------- ---- ----
 list char [100] [12] char(*)[12](指向12元素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; } } 

我写了一个阅读线条的function。 我认为它应该是安全的。

检查:io_readline

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