如何使用scanf和fgets来读取文件

时间:2016-08-22 11:28:33

标签: c scanf fgets

我需要阅读以下文本文件:

2 2
Kauri tree
Waterfall
0 0 W S
0 1 E N

我想使用scanf获取第一行,然后使用fgets作为第二行和第三行,然后再使用scanf作为其余行。< / p>

我写了这样的代码:

#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获取第二行。但我不知道该怎么做。

3 个答案:

答案 0 :(得分:2)

除非明确初始化,否则在函数内定义的局部变量将具有不确定值。对于指针,这意味着他们指向一个看似随机的位置。使用任何未初始化的变量,除了初始化它之外,都会导致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

输入存储在内存中的缓冲区中,然后scanffgets从此缓冲区中读取。通过上面的输入,缓冲区将看起来像这样

+----+----+----+----+----+----+----+----+----+
|  2 |  2 | \n |  h |  e |  l |  l |  o | \n |
+----+----+----+----+----+----+----+----+----+

scanf调用之后读取输入缓冲区的两个数字

+----+----+----+----+----+----+----+
| \n |  h |  e |  l |  l |  o | \n |
+----+----+----+----+----+----+----+

那么循环中第一次调用fgets会看到换行符。所以它读取了换行符然后就完成了,将字符串"hello\n"留在缓冲区中,以便第二次调用fgets

有几种方法可以解决这个问题。我个人更喜欢的是使用fgets普遍读取行,如果您需要对行进行简单解析,请使用sscanf(注意前导s,也请{{1} 3}})这样做。

另一种方法是简单地从输入中读取字符,一次读取一个字符,然后丢弃它们。当您阅读换行符时,请停止循环并继续执行该程序的其余部分。

答案 1 :(得分:1)

考虑以下示例,其中注释解释了一些重要的观点:

#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;
}

当您使用scanffscanf阅读数据时,您可以检查结果,例如:

    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”,所以我们检查“比较返回值4”

答案 2 :(得分:1)

我已经解决了我的问题。我不应该使用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] = {'\0'};

    for (int i = 0; i < node; i++) {
        fgets(str, NUM_OF_CHAR, fp);
    }
    printf("%s", str);
}