我有一个文件,其中包含有关此类电影的信息:
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); 存储所需的信息和空格。该怎么做只会存储电影和制片人的名字。
答案 0 :(得分:3)
它只是进入一个infinte循环
那是因为它是一个无限循环。你有while(1)
没有休息条件。它应该在它不能再读取任何行之后就会中断。
每次使用文件(即fopen
,fgets
和fscanf
时,都需要检查操作是否成功。如果失败,代码将继续处理结果中的任何垃圾。
这对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.nimed
和db.rezi
分配1024个更为理想,但我想展示更一般的情况读入的东西会留在那里。
while(fgets(line, 1024, f1) != NULL)
确保我读到文件结尾。然后使用line
语句处理switch
,具体取决于接下来要播放的行类型。这使得从文件中读取的过程分离,这可能是不可预测的并且需要大量的错误检查,从处理数据更容易一些。从技术上讲,我应该检查那些sscanf
是否成功,但我很懒。 :)