在C中从文件读入结构
我正在为大学做一个小项目(第一学期做一个书店实现),我有一个从文本文件读入结构列表的问题,其中有一个存储作者的二维字符数组。但是,它不能正常工作(每次启动程序时,它都会显示列表为空)。写入文件是有效的(我认为,因为它会用空数据覆盖我的文本文件) 示例数据:在C中从文件读入结构,c,C,我正在为大学做一个小项目(第一学期做一个书店实现),我有一个从文本文件读入结构列表的问题,其中有一个存储作者的二维字符数组。但是,它不能正常工作(每次启动程序时,它都会显示列表为空)。写入文件是有效的(我认为,因为它会用空数据覆盖我的文本文件) 示例数据: Adam Mickiewicz///Pan Tadeusz/Publisher 1/1833/24.99 Jules Verne///Around The World in 80 days/Publisher 1/1904/19.99 Jea
Adam Mickiewicz///Pan Tadeusz/Publisher 1/1833/24.99
Jules Verne///Around The World in 80 days/Publisher 1/1904/19.99
Jean-Jacques Sempe/Rene Goscinny//Little Nicholas/Publisher 2/1963/22.99
我的结构:
#define AK 3 // Constant denoting max number of authors
typedef struct
{
char authors[AK][100];
char title[255];
char publisher[100];
unsigned int year;
double price;
struct Book *next;
} Book;
Book *first; // Address of the first element in a list
从文件中读取:
void read_from_file(const char *path)
{
int no_of_authors;
int i;
printf("Loading...\n");
FILE *fp = fopen(path, "r"); // Opening a file
// Checking for errors
if (!fp) {
printf("Error reading from file!");
return;
}
// The loading mechanism
no_of_authors = 0;
while (!feof(fp)) {
Book *new = (Book*) malloc(sizeof(Book));
for (i = 0; i < AK; i++) {
fscanf(fp, "%s/", new->authors[i]);
}
fscanf(fp, "%s/%s/%u/%lf", new->title, new->publisher,
&new->year, &new->price);
fscanf(fp, "\n");
new = new->next;
}
fclose(fp);
printf("Loading successful.");
}
void read_from_文件(const char*path)
{
int无作者;
int i;
printf(“加载…\n”);
FILE*fp=fopen(路径,“r”);//打开文件
//检查错误
如果(!fp){
printf(“读取文件时出错!”);
返回;
}
//加载机制
作者数量=0;
而(!feof(fp)){
Book*new=(Book*)malloc(sizeof(Book));
对于(i=0;i作者[i]);
}
fscanf(fp,“%s/%s/%u/%lf”,新建->标题,新建->发布者,
&新->年,&新->价格);
fscanf(fp,“\n”);
新建=新建->下一步;
}
fclose(fp);
printf(“加载成功”);
}
写入文件(以防万一):
void write_to_文件(const char*path,Book*first)
{
int i;
printf(“保存…\n”);
文件*fp=fopen(路径,“w”);
账面*当前=第一;
如果(!fp){
printf(“打开文件时出错!”);
dump_list(first);//转储列表以防止内存泄漏,这样可以正常工作
}
//储蓄机制
while(第一个!=NULL){
对于(i=0;i作者[i]);
}
fprintf(fp,“%s/%s/%u/%lf”,当前->标题,当前->发布者,
&当前->年度,&当前->价格);
fprintf(fp,“\n”);
}
fclose(fp);
printf(“保存成功”);
}
OP最大的失败是没有检查fscanf()的返回值。
。如果代码这样做,问题将被更快地检测出来
在读取数据行时,首先要考虑的是: 输入可能有问题吗 对于学习者应用程序,这通常被认为是不正确的。输入要么“良好”,要么文件结束。我们不要假设数据的格式太好 事实证明,虽然数据文件可能没有错误,但读取它的代码可能是错误的。代码检查
*scanf()
返回值的第二个微妙原因-自我验证
对于面向行的数据,使用
fgets()
读取数据行比使用feof()、fscanf()…
读取数据行要好得多,另请参见
使用
“%s”
读取不包含空格的文本。这也将读入'/'
。由于'/'
用于定界,“%s”
不是可接受的输入说明符。像“亚当·米奇维茨”这样的名字还包括一个空格。不使用“%s”
的第二个原因
考虑一下fscanf(fp,“%s/”,new->authors[i])代码>正在执行<代码>%s“
扫描到新建->作者[i]
非空白字符。非空格字符后的字符是空格,而不是'/'
使用“%[^/]”
读取不包含“/”
的文本
使用“%n”
跟踪当前扫描偏移量
现在分析这行代码
char *p = buf;
Book *nu = malloc(sizeof *nu); // no cast needed
for (size_t i = 0; i < AK; i++) {
int n = 0;
sscanf(p, "%99[^/]/%n", nu->authors[i], &n);
if (n == 0) {
Handle_Parse_Error();
}
p += n;
}
if (sscanf(p, "%254[^/]/%99[^/]/%u/%lf",
nu->title, nu->publisher, &nu->year, &nu->price) != 4) {
Handle_Parse_Error();
}
如果你用整数来计算价格,而不是用双精度(或类似的货币和次货币)来计算美元,事情就会变得简单起来。
new=new->next代码>:你在哪里给new->next
?struct Book{}赋值;与struct{}Book不同<代码>而(!feof(fp))
是错误的:它告诉您上次读取没有失败,但没有告诉您下次读取不会失败。typedef struct{…}Book
定义匿名结构类型,并将其定义为type nameBook
<代码>结构书*下一步定义了一个指向类型struct Book
的不透明指针,您从未定义过它(但这是可以的,因为它是一个指针,不需要知道大小)。一般来说,避免使用structstypedef
,因为它在保存一些键入的同时,也会造成混乱(如本文所示)。
char buf[sizeof(Book) * 2]; // Use an ample sized buffer
while (fgets(buf, sizeof buf, fp)) {
char *p = buf;
Book *nu = malloc(sizeof *nu); // no cast needed
for (size_t i = 0; i < AK; i++) {
int n = 0;
sscanf(p, "%99[^/]/%n", nu->authors[i], &n);
if (n == 0) {
Handle_Parse_Error();
}
p += n;
}
if (sscanf(p, "%254[^/]/%99[^/]/%u/%lf",
nu->title, nu->publisher, &nu->year, &nu->price) != 4) {
Handle_Parse_Error();
}
if (nu->year < -6000 || nu->year > 2999) Fail_Year_Range();
Book *read_from_file(const char *path) {
Book first; // Only the .next member is used.
first.next = NULL;
Book *current = &first;
// setup while () loop
...
while (fgets(buf, sizeof bu, fp)) {
...
Book *nu = malloc(sizeof *nu);
nu->next = NULL;
...
// parse data, fill in rest of nu->
....
current->next = nu; // update current pointer
current = nu;
}
// close up file ...
return first.next;
}