如何用C语言逐行读取文件?
我有一个最多包含 100 个 IP 地址的文本文件,每行 1 个。我需要将每个地址作为字符串读取到名为“list”的数组中。首先,我假设“list”需要是一个二维字符数组。每个IP地址的长度是11个字符,如果包含'\0'则为12个字符,所以我声明list如下:
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++;
}
它现在打印五个字符串,但它们是内存中的“随机”字符。
I have a text file with up to 100 IP addresses, 1 per line. I need to read each address, as a string, into an array called "list". First, I'm assuming that "list" will need to be a two-dimensional char array. Each IP address is 11 characters in length, 12 if you include '\0', so I declared list as follows:
char list[100][12];
Next, I attempt to use fgets to read the stream:
for (i = 0; i < 100; i++)
{
if (feof(stream))
break;
for (j = 0; j < 12; j++)
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++)
{
for (j = 0; j < 11; j++)
printf("%c", list[i][j]);
printf("\n");
}
After running the program, it's clear something is wrong. Being a beginner, I'm not sure what, but I'm guessing I'm reading the file wrong. There are no errors. It compiles, but prints a strange address on two lines.
Edit:
I replaced the fgets code with this:
for (i = 0; i < 100; i++)
{
if (feof(stream))
break;
fgets(list[i], 12, stream);
count++;
}
It now prints five strings, but they are "random" characters from memory.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
对
fgets
的调用可将最多 11 个字符从流读取到数组中。所以您不想为每个字符串的每个字符调用一次。想想这些循环:当 i=0 和 j=0 时,它会读取最多 11 个字符到
&list[0][0]
。然后,当 i=0 且 j=1 时,它会读取另外 11 个字符到&list[0][1]
。这是错误的,原因有两个:它会覆盖最后一次调用的结果,并且可能写入的字节数超出了 list[0] 可以容纳的字节数。Your call to
fgets
reads up to 11 characters from the stream into the array. So you don't want to be calling that once for each character of each string.Just think about those loops: with i=0 and j=0, it reads up to 11 characters to
&list[0][0]
. Then with i=0 and j=1, it reads another 11 characters to&list[0][1]
. That's wrong for two reasons - it overwrites the result of the last call, and potentially it writes more bytes than list[0] can hold.您可能会在第一次调用 fgets 时读取前 12 个字符,然后第二次调用将捕获换行符,然后第三次调用将获取下一行。
尝试使用具有 15 个字符限制的 fgets,并扩展缓冲区。
You may be reading the first 12 characters in the first call the fgets, then the second call will catch the newline, then the third call gets the next line.
Try using fgets with a 15 character limit, and expanding your buffer.
第二个循环是不必要的,它会破坏你的记忆。你应该做这样的事情,
The second loop is not necessary and it corrupts your memory. You should do something like this,
对于 (i = 0; i < 100; i++)
{
}
for (i = 0; i < 100; i++)
{
}
不要使用
feof()
作为循环条件;直到您尝试读取文件末尾之后,它才会返回 true,这意味着您的循环将执行太多次。检查输入调用的结果(无论使用fgets()
还是fscanf()
)以查看是否成功,然后检查feof()
如果出现错误情况。fgets()
读取整个字符串,而不是单个字符,因此您不想传递字符串中每个单个字符的地址。可以这样称呼它:现在,对于 Bode 关于数组和指针的惯常讲授...
当数组表达式出现在大多数上下文中时,表达式的类型会从“T 的 N 元素数组”隐式转换为“指向的指针” T”,表达式的值为数组第一个元素的地址。此规则的例外情况是,数组表达式是
sizeof
或&
运算符的操作数,或者是在数组中用作初始值设定项的字符串文字。宣言。当你听到人们说“数组和指针是同一件事”时,他们就是在混淆这条规则。数组和指针是完全不同的动物,但它们在某些情况下可以互换使用。请注意,在上面的代码中,我将
list[i]
作为第一个参数传递给 fgets(),没有任何修饰(例如&
运算符)。尽管list[i]
的类型是“12 元素 char 数组”,但在这种情况下,它会隐式转换为“指向 char 的指针”类型,并且该值将是 <代码>列表[i][0]。请注意,我还将相同的表达式传递给sizeof
运算符。在这种情况下,数组表达式的类型不会转换为指针类型,并且 sizeof 运算符返回数组类型中的字节数 (12)。只是为了明确一下:
这一切意味着
fgets()
将读取接下来的 12 个字符(前提是它不会首先遇到换行符或 EOF)并将其存储在>列表[i][0]
。请注意,fgets()
会将终止 nul 字符 (0) 写入字符串末尾。另请注意,如果fgets()
遇到换行符并且目标数组中有足够的空间容纳它和终止 nul,fgets()
将存储nul 字符之前的终止换行符。因此,如果您的输入文件有这样的行,则读取后输入缓冲区的内容将为
"1.1.1.1\n\0xxx"
其中x
是某个随机值。如果您不希望出现换行符,可以使用strchr()
函数找到它,然后用 0 覆盖它:因为
fgets()
在下一个换行符,并且由于您的输入缓冲区大小为 12 个字符,因此您可能会遇到这样的情况:文件中的下一个输入字符是换行符;在这种情况下,fgets() 只会将该换行符写入输入缓冲区,因此您将有一些空条目,这可能不是您想要的。您可能需要向输入缓冲区添加一个额外的字节以避免这种情况。把它们放在一起:
Do not use
feof()
as your loop condition; it will not return true until after you've tried to read past the end of the file, meaning your loop will execute one time too many. Check the result of your input call (whether you usefgets()
orfscanf()
) to see if it succeeded, then checkfeof()
if you got an error condition.fgets()
reads entire strings, not individual characters, so you don't want to pass the address of each individual character in your string. Call it like so instead:And now, for Bode's usual spiel about arrays and pointers...
When an array expression appears in most contexts, the type of the expression is implicitly converted from "N-element array of T" to "pointer to T", and the value of the expression is the address of the first element of the array. The exceptions to this rule are when the array expression is the operand of the
sizeof
or&
operators, or it is a string literal that is being used as an initializer in a declaration. When you hear people say "arrays and pointers are the same thing", they're garbling that rule. Arrays and pointers are completely different animals, but they can be used interchangeably in some contexts.Note that in the code above I passed
list[i]
as the first argument to fgets() without any decoration (such as the&
operator). Even though the type oflist[i]
is "12-element array of char", in this context it is implicitly converted to type "pointer to char", and the value will be the address oflist[i][0]
. Note that I also passed that same expression to thesizeof
operator. In that case, the type of the array expression is not converted to a pointer type, and the sizeof operator returns the number of bytes in the array type (12).Just to nail it down:
What all this means is that
fgets()
will read up to the next 12 characters (provided it doesn't hit a newline or EOF first) and store it starting atlist[i][0]
. Note thatfgets()
will write a terminating nul character (0) to the end of your string. Note also that iffgets()
encounters a newline and there's room in the target array for it and the terminating nul,fgets()
will store the terminating newline before the nul character. So if your input file has a line likethen the contents of your input buffer after the read will be
"1.1.1.1\n\0xxx"
wherex
is some random value. If you don't want the newline there, you can use thestrchr()
function to find it and then overwrite it with a 0:Since
fgets()
stops at the next newline, and since your input buffer is sized for 12 characters, it's possible for you to run into a situation where you have a newline as the next input character in the file; in that case,fgets()
will write only that newline to the input buffer, so you'll have some empty entries, which is probably not what you want. You might want to add an extra byte to your input buffer in order to avoid that situation.Putting it all together:
我写了一个读取行的函数。我想应该是安全的。
检查:io_readline
https://github.com/arhuaco/junkcode /blob/master/junk/misc/atail.c
I wrote a function for reading lines. I think it should be safe.
Check : io_readline
https://github.com/arhuaco/junkcode/blob/master/junk/misc/atail.c
首先,阅读:
你这里有一个大问题。这是尝试将字符串读入数组中的每个连续字符。
总而言之,我认为你让事情变得比需要的复杂得多。将您的数组视为 100 个字符串,
fgets
将一次处理一个字符串。这意味着读取看起来像这样:还有一个小细节需要处理:
fgets()
通常会在每行末尾保留换行符。因此,您可能需要留出 13 个字符的空间(11 个字符用于地址,1 个用于换行符,1 个用于 NUL 终止符),否则您可能需要将数据读入临时缓冲区,并将其复制到您的list
仅在您删除换行符之后。在当前用于打印字符串的代码中,您一次处理一个字符,这可以工作,但不必要地困难。有几个人建议使用 %s printf 转换,这本身就很好。然而,为了配合它,您必须稍微简化索引。打印前六个地址将如下所示:
First, reading:
You have a big problem right here. This is attempting to read a string into each successive character in your array.
All in all, I think you're making this a lot more complex than it needs to be. Think of your array as 100 strings, and
fgets
will work with a string at a time. That means reading can look something like this:There is one other minor detail to deal with:
fgets()
normally retains the new-line at the end of each line. As such, you may need to leave room for 13 characters (11 for address, 1 for new-line, 1 for NUL terminator), or else you may want to read the data into a temporary buffer, and copy it to yourlist
only after you've stripped off the new-line.In your current code for printing the strings, you're working one character at a time, which can work, but is unnecessarily difficult. Several people have suggested using the %s printf conversion, which is fine in itself. To go with it, however, you have to simplify your indexing a bit. Printing the first six addresses would look something like this: