[英]Segmentation Fault in C using fgets(), strtok(), strcpy()
我从下面显示的代码中遇到段错误。 我正在尝试创建一个简单的数据库,该数据库从bin文件中读取标准输入,并用逗号将其砍掉,然后将每个值放入数组中,然后将其放入结构中。 我想对标准输入中的每一行执行此操作,然后将结构最后写入文件。
我从main
调用此loadDatabase
函数。
数据看起来像下面一行一样,大约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;
}
您的代码中存在多个问题:
没有提供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;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.