使用fgets存储字符串

时间:2017-04-20 18:06:58

标签: c

我有一个文件,其中包含有关此类电影的信息:

Film code
Name
Year of release
Movie length(in minutes)
The film producer

我必须从文件中读取此信息并将该信息存储到指针中。到目前为止我的代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct filmiab
{
    int koodpk;
    char *nimed;
    int aasta;
    int kestus;
    char *rezi;
} filmiab;

int main()
{
    filmiab *db;

    FILE *f1;
    f1 = fopen("filmid.txt", "r");

    db->nimed = (char*)malloc(sizeof(db->nimed) * sizeof(char));
    db->rezi = (char*)malloc(sizeof(db->rezi) * sizeof(char));

    while(1)
    {
        fscanf(f1, "%d ", &db->koodpk);
        fgets(db->nimed, 100, f1);
        db->nimed = (char*)realloc(db->nimed, sizeof(char) * sizeof(db->nimed)); //gets more memory to store the strings
        fscanf(f1, "%d %d ", &db->aasta, &db->kestus);
        fgets(db->rezi, 100, f1);
        db->rezi = (char*)realloc(db->rezi, sizeof(char) * sizeof(db->rezi));

        printf("Filmi kood: %d\nFilmi nimi: %sAasta: %d\nKestus minutites: %d\nFilmi rezis66r: %s\n",
        db->koodpk, db->nimed, db->aasta, db->kestus, db->rezi);
        printf("\n");
    }

    return 0;
}

它只是进入一个infinte循环,只打印最后5行。我知道在使用fgets时它会用最后5行替换所有字符串。 但是我能做什么呢,它会存储所有信息,所以我可以在另一个函数中打印出来(或者只是使用它们)。为什么它会进入无限循环?

编辑: 我只需要使用结构中创建的指针。

EDIT2: 现在这两条线     fgets(db-> nimed,100,f1);     fgets(db-&gt; rezi,100,f1); 存储所需的信息和空格。该怎么做只会存储电影和制片人的名字。

1 个答案:

答案 0 :(得分:3)

  

它只是进入一个infinte循环

那是因为它是一个无限循环。你有while(1)没有休息条件。它应该在它不能再读取任何行之后就会中断。

每次使用文件(即fopenfgetsfscanf时,都需要检查操作是否成功。如果失败,代码将继续处理结果中的任何垃圾。

这对fscanf尤其是一个问题,因为如果它失败了,它会将文件指针留在原来的位置,并且可能会一遍又一遍地重新扫描同一行。一般来说,avoid scanf and fscanf。相反,fgets整行,以确保它被读取,并使用sscanf进行扫描。

另一个问题是你如何分配内存是不对的。

filmiab *db;

这会将指针放在堆栈上,但它指向垃圾。没有为实际结构分配内存。

db->nimed = (char*)malloc(sizeof(db->nimed) * sizeof(char));

sizeof(db->nimed)不是db->nimed中字符串的长度,而是指针的大小。可能是4或8.所以你只分配了4或8个字节。

fgets(db->nimed, 100, f1);

然后用fgets读取最多100个字节,可能导致缓冲区溢出。

db->nimed = (char*)realloc(db->nimed, sizeof(char) * sizeof(db->nimed));

然后你重新分配太少,太迟了。同样,与之前相同,这仅分配4或8个字节。它可能什么都不做,因为内存已经很大了。

要解决这个问题,首先要将整个结构放在堆栈上。

filmiab db;

然后为其字符串分配必要的空间。请注意,由于sizeof(char)始终为1,因此无需包含它。 There's also no need to cast the result of malloc

db.nimed = malloc(100);
db.rezi = malloc(100);

现在没有必要重新分配,你已经获得了100个字节的内存并可以用fgets写入。

以备将来参考,以下是我如何重做此事。

int main() {
    filmiab db;

    char file[] = "filmid.txt";
    FILE *f1 = fopen(file, "r");
    if( f1 == NULL ) {
        fprintf( stderr, "Could not open %s for reading: %s", file, strerror(errno) );
    }

    char line[1024];
    int state = 0;

    while(fgets(line, 1024, f1) != NULL) {
        switch(state % 5) {
            case 0:
                sscanf(line, "%d", &db.koodpk);
                break;
            case 1:
                db.nimed = strdup(line);
                break;
            case 2:
                sscanf(line, "%d", &db.aasta);
                break;
            case 3:
                sscanf(line, "%d", &db.kestus);
                break;
            case 4:
                db.rezi = strdup(line);
                printf("Filmi kood: %d\nFilmi nimi: %sAasta: %d\nKestus minutites: %d\nFilmi rezis66r: %s\n",
                        db.koodpk, db.nimed, db.aasta, db.kestus, db.rezi);
                printf("\n");
                break;
            default:
                // Should never get here
                assert(0);
                break;
        }

        state++;
    }

    return 0;
}

有一个大的行缓冲区可以重复使用,它是1K,但它只有1K一次。 strdup重复字符串,但只分配足够的内存来保存字符串。这样就无需预测大线的大小,也避免了大量重新分配内存碎片。

在这种特殊情况下,由于db正在重复使用,因此为db.nimeddb.rezi分配1024个更为理想,但我想展示更一般的情况读入的东西会留在那里。

while(fgets(line, 1024, f1) != NULL)确保我读到文件结尾。然后使用line语句处理switch,具体取决于接下来要播放的行类型。这使得从文件中读取的过程分离,这可能是不可预测的并且需要大量的错误检查,从处理数据更容易一些。从技术上讲,我应该检查那些sscanf是否成功,但我很懒。 :)