我从下面的代码中得到了一个seg错误。我试图创建一个简单的数据库,从bin文件中读取stdin,用逗号将其删除,将每个值抛出到一个数组中并将其抛出到结构中。我想对标准输入中的每一行执行此操作,然后在最后将结构写入文件。
我从loadDatabase
调用此main
函数。
数据在一行中如下所示,长约14行:
34156155,MILES,NORMA,TAMMY,n/a,9/16/1964,FEMALE,123-45-6789,LAGUARDIA RD,SHLT,10915,n/a,n/a,CHESTER,NY,848-896-8296,n/a,NMILES@AMGGT.COM,n/a
这是我目前的代码。如果我的C非常糟糕,请原谅。第一次......:
struct _record {
char ID[25];
char lname[25]; // last name
char fname[25]; // first name
char mname[25]; // middle name
char suffix[25];
char bday[25];
char gender[25];
char SSN[25];
char add1[25]; //address 1
char add2[25]; //address 2
char zip[25];
char maiden[25];
char MRN[25];
char city[25];
char state[25];
char phone1[25];
char phone2[25];
char email[25];
char alias[25];
};
bool loadDatabase(char *db_name) {
printf("Loading Database...");
char buffer[400];
FILE *fp;
int x;
fp = fopen(db_name, "wb"); //write & binary option
if (fp == NULL) {
puts(" ERROR: FILE CANNOT BE OPENED");
return false;
} else {
struct _record record;
while (fgets(rec, sizeof(rec), stdin) != NULL) {
value = strtok(NULL, ",");
flds[0] = strdup(value);
//load lname
value = strdup(NULL, ",");
flds[1] = strdup(value);
// load fname
value = strdup(NULL, ",");
flds[2] = strdup(value);
// load mname
value = strtok(NULL, "\n");
flds[3] = strdup(value);
// did not write the rest bc of the seg fault
strcpy(record.ID, flds[0]);
strcpy(record.lname, flds[1]);
strcpy(record.fname, flds[2]);
strcpy(record.mname, flds[3]);
strcpy(record.suffix, flds[4]);
strcpy(record.bday, flds[5]);
strcpy(record.gender, flds[6]);
strcpy(record.SSN, flds[7]);
strcpy(record.add1, flds[8]);
strcpy(record.add2, flds[9]);
strcpy(record.zip, flds[10]);
strcpy(record.maiden, flds[11]);
strcpy(record.MRN, flds[12]);
strcpy(record.city, flds[13]);
strcpy(record.state, flds[14]);
strcpy(record.phone1, flds[15]);
strcpy(record.phone2, flds[16]);
strcpy(record.email, flds[17]);
strcpy(record.alias, flds[18]);
}
printf("ID: %s", record.ID);
fwrite(record, sizeof(struct _record), 1, fp);
fclose(fp);
}
return true;
}
答案 0 :(得分:3)
您的代码中存在多个问题:
未提供fld
的定义。它应该被定义为19 char *
:
char *fld[19];
你有一些剪切+粘贴错误:value = strdup(NULL, ",");
而不是value = strtok(NULL, ",");
缺少许多行。
您永远不会测试strtok()
是否返回NULL
。输入无效将导致未定义的行为
字符串的内存是不必要的:您可以直接将字符串复制到记录字段中。
在使用strcpy
复制字符串之前,不检查字符串的长度。输入无效可能导致缓冲区溢出。
fwrite
的参数应该是记录的地址,而不是它的值:
fwrite(&record, sizeof(struct _record), 1, fp);
使用strtok()
(或sscanf()
和%[^,]
转换说明符)无法正确处理空字段:strtok()
会考虑,
的任何序列作为单个分隔符(%[^,]
也不匹配空字段)。我建议使用一个函数。
应在每行之前清除record
结构,以避免将未初始化的内容存储到数据库文件中。
为了避免其中一些问题,您应该提高警告级别,让编译器为常见的编程错误生成诊断:gcc -Wall -Wextra -Werror
或clang -Weverything
或cl /W4
。
以下是改进版本:
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
struct _record {
char ID[25];
char lname[25]; // last name
char fname[25]; // first name
char mname[25]; // middle name
char suffix[25];
char bday[25];
char gender[25];
char SSN[25];
char add1[25]; //address 1
char add2[25]; //address 2
char zip[25];
char maiden[25];
char MRN[25];
char city[25];
char state[25];
char phone1[25];
char phone2[25];
char email[25];
char alias[25];
};
bool loadField(char *dest, int size, char **cursorp) {
bool truncated = false;
int i = 0;
char *p;
for (p = *cursorp; *p != '\0' && *p != '\n'; p++) {
if (*p == ',') {
p++; // skip the comma separator
break;
}
if (i + 1 < size) {
dest[i] = *p;
} else {
truncated = 1;
}
i++;
}
// pad the field with null bytes
while (i < size) {
dest[i++] = '\0';
}
*cursorp = p;
if (truncated) {
fprintf(stderr, "field too long: %.*s\n", i, *cursorp);
return false;
} else {
return true;
}
}
bool loadDatabase(const char *db_name) {
char buffer[1000];
FILE *fp;
printf("Loading Database...");
fp = fopen(db_name, "wb"); //write & binary option
if (fp == NULL) {
fprintf(stderr, "error: cannot open file %s: %s\n", db_name, strerror(errno));
return false;
} else {
struct _record record; // clear the record
while (fgets(buffer, sizeof(buffer), stdin) != NULL) {
char *cursor = buffer;
memset(&record, 0, sizeof(record)); // clear the record
loadField(record.ID, sizeof(record.ID), &cursor);
loadField(record.lname, sizeof(record.lname), &cursor);
loadField(record.fname, sizeof(record.fname), &cursor);
loadField(record.mname, sizeof(record.mname), &cursor);
loadField(record.suffix, sizeof(record.suffix), &cursor);
loadField(record.bday, sizeof(record.bday), &cursor);
loadField(record.gender, sizeof(record.gender), &cursor);
loadField(record.SSN, sizeof(record.SSN), &cursor);
loadField(record.add1, sizeof(record.add1), &cursor);
loadField(record.add2, sizeof(record.add2), &cursor);
loadField(record.zip, sizeof(record.zip), &cursor);
loadField(record.maiden, sizeof(record.maiden), &cursor);
loadField(record.MRN, sizeof(record.MRN), &cursor);
loadField(record.city, sizeof(record.city), &cursor);
loadField(record.state, sizeof(record.state), &cursor);
loadField(record.phone1, sizeof(record.phone1), &cursor);
loadField(record.phone2, sizeof(record.phone2), &cursor);
loadField(record.email, sizeof(record.email), &cursor);
loadField(record.alias, sizeof(record.alias), &cursor);
printf("ID: %s\n", record.ID);
if (fwrite(&record, sizeof(record), 1, fp) != 1) {
fprintf(stderr, "error: cannot write record: %s\n", strerror(errno));
break;
}
}
fclose(fp);
}
return true;
}
int main(void) {
if (loadDatabase("database.bin"))
return 1;
return 0;
}