复制到二进制文件时出现分段错误
Segmentation fault when I copy to binary file
我有作业。从使用
格式的文本文件复制
name birthday street
NAME0;DAY/MONTH/YEAR;STREET0;
NAME1;DAY/MONTH/YEAR;STREET1;
NAME2;DAY/MONTH/YEAR;STREET2;
以上信息按年份排序。排序后,所有内容都必须复制到二进制文件中。它一直给我 Segmentation Fault 。我认为问题是 token
用于读取每一行的所有信息。
编辑:
使用调试器,我发现第 167 行当 i = 4 时导致错误。 strcpy(stud[i].day,token);
编辑:
我认为问题出在输入文件中。我重新创建了,现在可以使用了。
void students_list(FILE *ifp , FILE *b_ifp)
{
char line[150] ;
char *token;
int i = 0 , n , t = 0 ;
memset(line,0,sizeof(line));
ifp = fopen("students.txt","r");
b_ifp = fopen("sorted.bin","wb");
struct students
{
char name[20] , street[50] , year[5] , month[3] , day[3];
};
typedef struct students students;
students stud[32], aux;
while(fgets(linie,sizeof(line), ifp) != NULL)
{
token = strtok(line,";");
strcpy(stud[i].name,token);
token = strtok(NULL,"/");
strcpy(stud[i].day,token);
token = strtok(NULL,"/");
strcpy(stud[i].month,token);
token = strtok(NULL,";");
strcpy(stud[i].year,token);
token = strtok(NULL,";");
strcpy(stud[i].street,token);
i++;
}
n = i;
while(t == 0)
{
t = 1;
for(i = 0 ; i < n ; i++)
{
if(strcmp(stud[i].year,stud[i + 1].year) > 0)
{
aux = stud[i];
stud[i] = stud[i + 1];
stud[i + 1] = aux;
t = 0;
}
}
}
for(i = 0; i < n ; i++)
{
fwrite(&stud[i], sizeof(stud[i]), 1, b_ifp);
}
fclose(ifp);
fclose(b_ifp);
}
基本上,问题可能有几个。您可以拥有一个包含超过 32
个学生的条目的文件。一开始您不会检查 strtok
中的 return 代码(如果您没有提供正确的格式并且没有更多字段,则可以是 NULL
)。如果 strtok
return 输入的值为 NULL
,您可以执行所有操作但拒绝添加记录。并检查数组大小(使用 while(n < 32 && fgets(...
之类的内容,就像对问题的一条评论所建议的那样)
类似于:
char *name, *street, *year, *month, *day;
name = strtok(line, ";"); if (!name) continue;
year = strtok(NULL, "/"); if (!year) continue;
month = strtok(NULL, "/"); if (!month) continue;
day = strtok(NULL, ";"); if (!day) continue;
/* a line has a \n at the end, so we try to eliminate it if last ; not present */
street = strtok(NULL, ";\n"); if (!street) continue;
然后,您需要遵守可能的长字段,因为 strcpy
不会并且可能导致您覆盖它们的末尾。如:
strncpy(stud[i].name, sizeof stud[i].name, name);
strncpy(stud[i].day, sizeof stud[i].day, day);
...
这还将检查以正确确定每个字符串的空字符,以防它由于 space 而必须截断字段(我认为您没有在字符串中为它提供 space stud
结构的字段)
最后,我建议,当您读取和排序核心内存中的所有数据时,只执行一次 fwrite
调用,例如:
res = fwrite(stud, sizeof stud[0], n, b_ifp);
而不是遍历所有记录。
另一个要讨论的问题是,如果您希望按数字或字典顺序对年份进行排序,因为年份肯定不会跨越 1950..2050
范围,这没有问题,但假设您必须订购1..1000
范围内的数字,按照字典顺序,首先是 1
,然后是 10
,然后是 100
,然后是 1000
,然后是 101
,然后是102
, ..., 然后 110
, 111
, 112
, ... 199
, 2
, 20
, 200
、201
、...,我想这不是您想要的。最好将数字转换为 int
并使用关系运算符进行排序。
我有作业。从使用
格式的文本文件复制name birthday street
NAME0;DAY/MONTH/YEAR;STREET0;
NAME1;DAY/MONTH/YEAR;STREET1;
NAME2;DAY/MONTH/YEAR;STREET2;
以上信息按年份排序。排序后,所有内容都必须复制到二进制文件中。它一直给我 Segmentation Fault 。我认为问题是 token
用于读取每一行的所有信息。
编辑:
使用调试器,我发现第 167 行当 i = 4 时导致错误。 strcpy(stud[i].day,token);
编辑: 我认为问题出在输入文件中。我重新创建了,现在可以使用了。
void students_list(FILE *ifp , FILE *b_ifp)
{
char line[150] ;
char *token;
int i = 0 , n , t = 0 ;
memset(line,0,sizeof(line));
ifp = fopen("students.txt","r");
b_ifp = fopen("sorted.bin","wb");
struct students
{
char name[20] , street[50] , year[5] , month[3] , day[3];
};
typedef struct students students;
students stud[32], aux;
while(fgets(linie,sizeof(line), ifp) != NULL)
{
token = strtok(line,";");
strcpy(stud[i].name,token);
token = strtok(NULL,"/");
strcpy(stud[i].day,token);
token = strtok(NULL,"/");
strcpy(stud[i].month,token);
token = strtok(NULL,";");
strcpy(stud[i].year,token);
token = strtok(NULL,";");
strcpy(stud[i].street,token);
i++;
}
n = i;
while(t == 0)
{
t = 1;
for(i = 0 ; i < n ; i++)
{
if(strcmp(stud[i].year,stud[i + 1].year) > 0)
{
aux = stud[i];
stud[i] = stud[i + 1];
stud[i + 1] = aux;
t = 0;
}
}
}
for(i = 0; i < n ; i++)
{
fwrite(&stud[i], sizeof(stud[i]), 1, b_ifp);
}
fclose(ifp);
fclose(b_ifp);
}
基本上,问题可能有几个。您可以拥有一个包含超过 32
个学生的条目的文件。一开始您不会检查 strtok
中的 return 代码(如果您没有提供正确的格式并且没有更多字段,则可以是 NULL
)。如果 strtok
return 输入的值为 NULL
,您可以执行所有操作但拒绝添加记录。并检查数组大小(使用 while(n < 32 && fgets(...
之类的内容,就像对问题的一条评论所建议的那样)
类似于:
char *name, *street, *year, *month, *day;
name = strtok(line, ";"); if (!name) continue;
year = strtok(NULL, "/"); if (!year) continue;
month = strtok(NULL, "/"); if (!month) continue;
day = strtok(NULL, ";"); if (!day) continue;
/* a line has a \n at the end, so we try to eliminate it if last ; not present */
street = strtok(NULL, ";\n"); if (!street) continue;
然后,您需要遵守可能的长字段,因为 strcpy
不会并且可能导致您覆盖它们的末尾。如:
strncpy(stud[i].name, sizeof stud[i].name, name);
strncpy(stud[i].day, sizeof stud[i].day, day);
...
这还将检查以正确确定每个字符串的空字符,以防它由于 space 而必须截断字段(我认为您没有在字符串中为它提供 space stud
结构的字段)
最后,我建议,当您读取和排序核心内存中的所有数据时,只执行一次 fwrite
调用,例如:
res = fwrite(stud, sizeof stud[0], n, b_ifp);
而不是遍历所有记录。
另一个要讨论的问题是,如果您希望按数字或字典顺序对年份进行排序,因为年份肯定不会跨越 1950..2050
范围,这没有问题,但假设您必须订购1..1000
范围内的数字,按照字典顺序,首先是 1
,然后是 10
,然后是 100
,然后是 1000
,然后是 101
,然后是102
, ..., 然后 110
, 111
, 112
, ... 199
, 2
, 20
, 200
、201
、...,我想这不是您想要的。最好将数字转换为 int
并使用关系运算符进行排序。