如何同时使用 scanf 和 fgets 读取文件
how to use both scanf and fgets to read a file
我需要阅读以下文本文件:
2 2
Kauri tree
Waterfall
0 0 W S
0 1 E N
我想使用 scanf
获取第一行,使用 fgets
获取第二行和第三行,然后再次使用 scanf
获取其余行.
我写的代码是这样的:
#include <stdio.h>
#define NUM_OF_CHAR 2
int main()
{
int node, edge;
scanf("%d %d", &node, &edge);
FILE* fp;
fp = stdin;
char* str[NUM_OF_CHAR]; //should be char str[NUM_OF_CHAR];
for (int i = 0; i < node; i++) {
fgets(str[i], 2, fp); //should be fgets(str, 2, fp);
}
printf("%s", str[0]); //printf("%s", str);
}
我输入的内容是:
2 2
hello
我得到了Segmentation fault
我在这里看到一个类似的问题,有人提到我可以调用 fgets
一次获取第一行但忽略它然后再次使用 fgets
获取第二行。但是我不知道怎么做。
考虑以下示例,其中注释解释了一些要点:
#include <stdio.h>
#define NUM_OF_CHAR 2
#define LEN_OF_STR 20
int main()
{
int node, edge;
FILE* fp;
fp = stdin;
char strbuf[LEN_OF_STR];
// stream is available after that
// reading numbers
fscanf(fp, "%d %d", &node, &edge);
// reading strings
for (int i = 0; i < node; i++) {
// reading line from input stream
fgets(strbuf, LEN_OF_STR, fp);
}
// cleaning input buffer
while (getchar() != '\n');
// reading lines with data
char str[NUM_OF_CHAR];
int a, b;
for (int i = 0; i < node; i++) {
// reading two numbers and two characters
fscanf(fp, "%d %d %c %c", &a, &b, &str[0], &str[1]);
// do something with dada, e.g. output
printf("%d %d %c %c\n", a, b, str[0], str[1]);
}
return 0;
}
当您使用scanf
或fscanf
读取数据时,您可以查看结果,例如:
if (4 == fscanf(fp, "%d %d %c %c", &a, &b, &str[0], &str[1]))
{
// actions for correct data
}
else
{
// actions for wrong input
}
此处格式行有 4 个说明符 - "%d %d %c %c",所以我们检查为 "compare return value with 4"
函数内部定义的局部变量,除非显式初始化,否则具有不确定值。对于指针,这意味着它们指向一个看似随机的位置。使用任何未初始化的变量,除非对其进行初始化,否则会导致 undefined behavior.
这里发生的是 fgets
将使用(未初始化且看似随机的)指针并使用它写入它指向的内存。在大多数情况下,此内存不属于您或您的程序,甚至可能会覆盖其他一些重要数据。这可能会导致崩溃或其他奇怪的行为或结果。
最简单的解决方案是 str
一个字符数组,例如
#define NUM_OF_STRINGS 2
#define STRING_LENGTH 64
...
char str[NUM_OF_STRINGS][STRING_LENGTH];
...
fgets(str[i], sizeof str[i], stdin);
您需要确保上面的 STRING_LENGTH
足以容纳每个字符串 包括 换行符和字符串终止符。在我上面显示的 64
的情况下,这意味着你最多可以有 62 个字符的行。
现在关于我指出的另一个问题,第一次调用 fgets
读取空行。
如果你有输入
2 2
hello
输入存储在内存中的一个缓冲区中,然后scanf
和fgets
从这个缓冲区中读取。带有上述输入的缓冲区看起来像这样
+----+----+----+----+----+----+----+----+----+
| 2 | 2 | \n | h | e | l | l | o | \n |
+----+----+----+----+----+----+----+----+----+
在 scanf
调用后读取输入缓冲区的两个数字
+----+----+----+----+----+----+----+
| \n | h | e | l | l | o | \n |
+----+----+----+----+----+----+----+
所以循环中对 fgets
的第一次调用将看到换行符。所以它读取换行符然后完成,将字符串 "hello\n"
留在缓冲区中以供 second 调用 fgets
.
有几种方法可以解决这个问题。我个人比较喜欢的是通用使用fgets
来读取行,如果你需要对行进行简单的解析,那么使用sscanf
(注意前导s
,也请see here for a good reference of all scanf
variants) 这样做。
另一种方法是简单地从输入中读取字符,一次一个字符,然后丢弃它们。当您读取换行符时,停止循环并继续程序的其余部分。
我的问题已经解决了。我不应该使用 char*
指针并使其指向一个数组。传递给 fgets
函数的第一个参数应该是 char*
,所以我应该只使用数组。
此外,由于 scanf
已经扫描了第一行,如果我接下来使用 fgets
,它会自动获取下一行。
#include <stdio.h>
#define NUM_OF_CHAR 100
int main()
{
int node, edge;
scanf("%d %d", &node, &edge);
FILE* fp;
fp = stdin;
char str[NUM_OF_CHAR] = {'[=10=]'};
for (int i = 0; i < node; i++) {
fgets(str, NUM_OF_CHAR, fp);
}
printf("%s", str);
}
我需要阅读以下文本文件:
2 2
Kauri tree
Waterfall
0 0 W S
0 1 E N
我想使用 scanf
获取第一行,使用 fgets
获取第二行和第三行,然后再次使用 scanf
获取其余行.
我写的代码是这样的:
#include <stdio.h>
#define NUM_OF_CHAR 2
int main()
{
int node, edge;
scanf("%d %d", &node, &edge);
FILE* fp;
fp = stdin;
char* str[NUM_OF_CHAR]; //should be char str[NUM_OF_CHAR];
for (int i = 0; i < node; i++) {
fgets(str[i], 2, fp); //should be fgets(str, 2, fp);
}
printf("%s", str[0]); //printf("%s", str);
}
我输入的内容是:
2 2
hello
我得到了Segmentation fault
我在这里看到一个类似的问题,有人提到我可以调用 fgets
一次获取第一行但忽略它然后再次使用 fgets
获取第二行。但是我不知道怎么做。
考虑以下示例,其中注释解释了一些要点:
#include <stdio.h>
#define NUM_OF_CHAR 2
#define LEN_OF_STR 20
int main()
{
int node, edge;
FILE* fp;
fp = stdin;
char strbuf[LEN_OF_STR];
// stream is available after that
// reading numbers
fscanf(fp, "%d %d", &node, &edge);
// reading strings
for (int i = 0; i < node; i++) {
// reading line from input stream
fgets(strbuf, LEN_OF_STR, fp);
}
// cleaning input buffer
while (getchar() != '\n');
// reading lines with data
char str[NUM_OF_CHAR];
int a, b;
for (int i = 0; i < node; i++) {
// reading two numbers and two characters
fscanf(fp, "%d %d %c %c", &a, &b, &str[0], &str[1]);
// do something with dada, e.g. output
printf("%d %d %c %c\n", a, b, str[0], str[1]);
}
return 0;
}
当您使用scanf
或fscanf
读取数据时,您可以查看结果,例如:
if (4 == fscanf(fp, "%d %d %c %c", &a, &b, &str[0], &str[1]))
{
// actions for correct data
}
else
{
// actions for wrong input
}
此处格式行有 4 个说明符 - "%d %d %c %c",所以我们检查为 "compare return value with 4"
函数内部定义的局部变量,除非显式初始化,否则具有不确定值。对于指针,这意味着它们指向一个看似随机的位置。使用任何未初始化的变量,除非对其进行初始化,否则会导致 undefined behavior.
这里发生的是 fgets
将使用(未初始化且看似随机的)指针并使用它写入它指向的内存。在大多数情况下,此内存不属于您或您的程序,甚至可能会覆盖其他一些重要数据。这可能会导致崩溃或其他奇怪的行为或结果。
最简单的解决方案是 str
一个字符数组,例如
#define NUM_OF_STRINGS 2
#define STRING_LENGTH 64
...
char str[NUM_OF_STRINGS][STRING_LENGTH];
...
fgets(str[i], sizeof str[i], stdin);
您需要确保上面的 STRING_LENGTH
足以容纳每个字符串 包括 换行符和字符串终止符。在我上面显示的 64
的情况下,这意味着你最多可以有 62 个字符的行。
现在关于我指出的另一个问题,第一次调用 fgets
读取空行。
如果你有输入
2 2
hello
输入存储在内存中的一个缓冲区中,然后scanf
和fgets
从这个缓冲区中读取。带有上述输入的缓冲区看起来像这样
+----+----+----+----+----+----+----+----+----+ | 2 | 2 | \n | h | e | l | l | o | \n | +----+----+----+----+----+----+----+----+----+
在 scanf
调用后读取输入缓冲区的两个数字
+----+----+----+----+----+----+----+ | \n | h | e | l | l | o | \n | +----+----+----+----+----+----+----+
所以循环中对 fgets
的第一次调用将看到换行符。所以它读取换行符然后完成,将字符串 "hello\n"
留在缓冲区中以供 second 调用 fgets
.
有几种方法可以解决这个问题。我个人比较喜欢的是通用使用fgets
来读取行,如果你需要对行进行简单的解析,那么使用sscanf
(注意前导s
,也请see here for a good reference of all scanf
variants) 这样做。
另一种方法是简单地从输入中读取字符,一次一个字符,然后丢弃它们。当您读取换行符时,停止循环并继续程序的其余部分。
我的问题已经解决了。我不应该使用 char*
指针并使其指向一个数组。传递给 fgets
函数的第一个参数应该是 char*
,所以我应该只使用数组。
此外,由于 scanf
已经扫描了第一行,如果我接下来使用 fgets
,它会自动获取下一行。
#include <stdio.h>
#define NUM_OF_CHAR 100
int main()
{
int node, edge;
scanf("%d %d", &node, &edge);
FILE* fp;
fp = stdin;
char str[NUM_OF_CHAR] = {'[=10=]'};
for (int i = 0; i < node; i++) {
fgets(str, NUM_OF_CHAR, fp);
}
printf("%s", str);
}