如何用C语言逐行读取文件?

发布于 2024-08-13 13:47:02 字数 941 浏览 5 评论 0原文

我有一个最多包含 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

给不了的爱 2024-08-20 13:47:03

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.

别把无礼当个性 2024-08-20 13:47:03

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

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

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

A newline character makes fgets stop reading, but it is considered a valid character and therefore it is included in the string copied to str.

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.

摘星┃星的人 2024-08-20 13:47:03

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

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

The second loop is not necessary and it corrupts your memory. You should do something like this,

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]);
}
左耳近心 2024-08-20 13:47:03

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

   if (feof(fp))
       break;

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

}

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

   if (feof(fp))
       break;

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

}

刘备忘录 2024-08-20 13:47:03

不要使用 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 的指针”类型,并且该值将是 <代码>列表[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)并将其存储在 >列表[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;
  }
}

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 use fgets() or fscanf()) to see if it succeeded, then check feof() if you got an error condition.

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() 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:

if (fgets(list[i], sizeof list[i], stream) != NULL)
{
  // process input address
}

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 of list[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 of list[i][0]. Note that I also passed that same expression to the sizeof 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:

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

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 at list[i][0]. Note that fgets() will write a terminating nul character (0) to the end of your string. Note also that if fgets() 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 like

1.1.1.1\n

then the contents of your input buffer after the read will be "1.1.1.1\n\0xxx" where x is some random value. If you don't want the newline there, you can use the strchr() function to find it and then overwrite it with a 0:

char *newline;
...
if ((newline = strchr(input[i], '\n')) != NULL)
{
  *newline = 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:

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;
  }
}
像你 2024-08-20 13:47:03

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

检查: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

云朵有点甜 2024-08-20 13:47:02

首先,阅读:

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

First, reading:

      for (j = 0; j < 12; j++)  
      fgets(&list[i][j], 12, stream);  

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:

for (i=0; i<100 && fgets(list[i], 11, string); i++)
    ;

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 your list 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:

for (i=0; i<6; i++)
    printf("%s", list[i]);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文