c带结构的错误访问fscanf
我正在读取一个文本文件,其格式如下: 名字姓氏年龄兄弟姐妹母亲年龄父亲年龄 导入头文件:c带结构的错误访问fscanf,c,arrays,string,struct,typedef,C,Arrays,String,Struct,Typedef,我正在读取一个文本文件,其格式如下: 名字姓氏年龄兄弟姐妹母亲年龄父亲年龄 导入头文件: #include <stdio.h> #include <string.h> #include <stdlib.h> #include <ctype.h> 其中People.txt看起来像: 爱尔兰的约翰·奥唐纳3 32.5 36.1 玛丽·麦克马洪M英格兰0 70 75 Peter Thompson F America 2 51 60如果你有一个指针字段c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
其中People.txt看起来像:
爱尔兰的约翰·奥唐纳3 32.5 36.1
玛丽·麦克马洪M英格兰0 70 75
Peter Thompson F America 2 51 60如果你有一个指针字段
char*full_name
,它意味着一个指针,应该由一些存在的对象初始化,在char*
的情况下,它通常应该是一个char数组。您可以通过两种方式进行修复:
- 只需创建一个数组字段,如
,并将字符串的最大长度传递给char full_name[100]
格式字符串,如scanf
,这是最简单的方法李>%100s
- 使用
函数,不要忘记malloc
释放该地址,或以其他方式为指针分配一些有效地址,例如,将数组声明为常用的自动存储变量,并为指针字段分配一个索引元素为零的地址,请记住,离开函数后,autostorage变量的地址将变为无效
%s
转换说明符告诉fscanf
读取单个单词,直到任何类似空格的空格字符,因此根据您的输入格式,您的全名
字段将一直读取到第一个空格,任何试图读取整数的尝试都将失败。fscanf()
遇到空白时将停止读取。如果是全名,则需要使用格式说明符%s
读取两个字符串%s
在找到空白后立即停止,因此它将只存储全名中的第一个名字,姓氏将转到第二个%s
,因此位于原产国
因此,如果您想阅读“Peter Thompson”,则需要引入两个字符串(字符数组)来存储名字和姓氏,然后将它们连接起来
但是,由于您希望读取字数不同的全名,我建议您使用(它还具有缓冲区溢出保护)。例如,“彼得·汤普森”有2个,而“玛丽·麦克·马洪”有3个。那么,如果您坚持使用fscanf()
,您会使用多少个%s
?2还是3?你不知道,这取决于你在运行时得到的输入。也许有一些正则表达式可以使用fscanf()
,但是相信使用fgets()
然后解析读取的文件行更适合于实践
现在我们用
fgets()
读取了一行文件,我们该怎么处理它呢?我们仍然不知道每个全名包含多少单词!如何找到答案?通过计算行中包含的空白。如果它包含w
空格,则它具有w+1
标记(在您的示例中可以是单词、数字或字符)
通过一个简单的if-else语句,我们可以在您的示例中区分这两种情况,即有6个空格(7个标记)和7个空格(8个标记表示“Mary Mc Mahon M England 0 70 75”)
现在,如何从字符串(行)提取到标记(全名、年龄等)?我们可以使用一个循环并使用一组if-else语句来表示,直到我找到第二个(或第三个,取决于空格的数量)空格,我将把当前标记附加到全名
。然后,下一个标记将是性,等等
当然你可以这样做,但因为我有点懒,我将根据你对fscanf()
的出色工作,使用sscanf()
来提取令牌。当然,使用这种方法,我们需要使用一个或两个(取决于空格的数量)额外的字符串,以便临时存储姓氏(在将其附加到名称之前)
最小完整工作示例:
#include <stdio.h>
#include <string.h>
#define P 1000 // Max number of people
#define L 256 // Max length of line read from file (-1)
typedef struct {
int person_ID; //not included in file
char full_name[32];
char sex[2];
char countryOfOrigin[16];
int num_siblings;
float parentsAges[2];
} PersonalInfo;
int count_whitespaces(char* str)
{
int whitespaces_count = 0;
while(*str)
{
if(*str == ' ')
whitespaces_count++;
str++;
}
return whitespaces_count;
}
void viewAllPersonalInformation(){
FILE* file = fopen("People.txt", "r");
if (file == NULL){
printf("File does not exist");
return;
}
int fileIsRead = 0;
int idCounter = 0;
PersonalInfo People[P];
// line of file, placeholder for biworded surnames, surname.
char line[L], str[8], surname[16];
//headers
// You have 7 format specifiers for the headers, but only 6 six in fscanf!!!
printf("%2s |%5s |%2s |%10s |%2s |%3s |%3s\n", "ID", "Name", "Sex", "Born In", "Number of siblings", "Mother's age", "Father's Age");
// read into 'line', from 'file', up to 255 characters (+1 for the NULL terminator)
while(fgets(line, L, file) != NULL) {
//fileIsRead = fscanf(file, "%s %s %s %s %d %f %f\n", People[idCounter].full_name, People[idCounter].full_name, People[idCounter].sex, People[idCounter].countryOfOrigin, &People[idCounter].num_siblings, &People[idCounter].parentsAges[0], &People[idCounter].parentsAges[1]);
// eat trailing newline of fgets
line[strcspn(line, "\n")] = 0;
// Skip empty lines of file
if(strlen(line) == 0)
continue;
if(count_whitespaces(line) == 6)
{
sscanf(line, "%32s %16s %c %16s %d %f %f", People[idCounter].full_name, surname, People[idCounter].sex, People[idCounter].countryOfOrigin, &People[idCounter].num_siblings, &People[idCounter].parentsAges[0], &People[idCounter].parentsAges[1]);
}
else // 7 whitespaces, thus 8 token in the string
{
sscanf(line, "%32s %8s %16s %c %16s %d %f %f", People[idCounter].full_name, str, surname, People[idCounter].sex, People[idCounter].countryOfOrigin, &People[idCounter].num_siblings, &People[idCounter].parentsAges[0], &People[idCounter].parentsAges[1]);
// Separate name and first word of surname with a space
strcat(People[idCounter].full_name, " ");
strcat(People[idCounter].full_name, str);
}
// Separate name and surname with a space
strcat(People[idCounter].full_name, " ");
strcat(People[idCounter].full_name, surname);
People[idCounter].person_ID = idCounter;
printf("%d %s %s %s %d %f %f\n", People[idCounter].person_ID, People[idCounter].full_name, People[idCounter].sex, People[idCounter].countryOfOrigin, People[idCounter].num_siblings, People[idCounter].parentsAges[0], People[idCounter].parentsAges[1]);
idCounter++;
if(idCounter == P)
{
printf("Max number of people read, stop reading any more data.\n");
break;
}
};
fclose(file);
printf("Finished reading file.\n");
}
int main() {
viewAllPersonalInformation();
return 0;
}
您注意到sscanf()
的格式说明符中的数字了吗?是的
动态内存分配怎么样 在上面的代码中,我估计了姓名、原产国等的最大长度。现在让这些尺寸动态变化怎么样?我们可以,但是我们仍然需要一个初步的估计
因此,我们可以在固定长度的临时数组中读取名称,然后使用查找字符串的实际长度。有了这些信息,我们现在可以动态地分配内存(通过char指针指向),然后将字符串从临时数组复制到其最终目的地。您能分享一个简单的例子吗?您传递到
fscanf
的指针是从未初始化的内存中读取的。@gsamaras抱歉,我没有完全理解一个简单的例子?我需要遍历文件,将数据分配给结构并打印。我尝试使用fscanf获取值,并使用printf打印它们。不确定该省略什么?@user2363025一个完整的最小示例,包括main、struct、读取数据之前为指针分配内存的方式以及读取数据的方式。根据你现在的问题,你可能会得出这样的结论:你忘记为指针分配内存了。是这样吗?一个完整的最低限度的例子不会留下任何疑问,也不需要假设;)@gsamaras您确实朝正确的方向跳了一跳:)我需要动态创建内存,但是作为起点,我修改了结构,使全名和国家是固定长度20,浮点数组是固定长度2。但程序仍在文件读取线上崩溃
#include <stdio.h>
#include <string.h>
#define P 1000 // Max number of people
#define L 256 // Max length of line read from file (-1)
typedef struct {
int person_ID; //not included in file
char full_name[32];
char sex[2];
char countryOfOrigin[16];
int num_siblings;
float parentsAges[2];
} PersonalInfo;
int count_whitespaces(char* str)
{
int whitespaces_count = 0;
while(*str)
{
if(*str == ' ')
whitespaces_count++;
str++;
}
return whitespaces_count;
}
void viewAllPersonalInformation(){
FILE* file = fopen("People.txt", "r");
if (file == NULL){
printf("File does not exist");
return;
}
int fileIsRead = 0;
int idCounter = 0;
PersonalInfo People[P];
// line of file, placeholder for biworded surnames, surname.
char line[L], str[8], surname[16];
//headers
// You have 7 format specifiers for the headers, but only 6 six in fscanf!!!
printf("%2s |%5s |%2s |%10s |%2s |%3s |%3s\n", "ID", "Name", "Sex", "Born In", "Number of siblings", "Mother's age", "Father's Age");
// read into 'line', from 'file', up to 255 characters (+1 for the NULL terminator)
while(fgets(line, L, file) != NULL) {
//fileIsRead = fscanf(file, "%s %s %s %s %d %f %f\n", People[idCounter].full_name, People[idCounter].full_name, People[idCounter].sex, People[idCounter].countryOfOrigin, &People[idCounter].num_siblings, &People[idCounter].parentsAges[0], &People[idCounter].parentsAges[1]);
// eat trailing newline of fgets
line[strcspn(line, "\n")] = 0;
// Skip empty lines of file
if(strlen(line) == 0)
continue;
if(count_whitespaces(line) == 6)
{
sscanf(line, "%32s %16s %c %16s %d %f %f", People[idCounter].full_name, surname, People[idCounter].sex, People[idCounter].countryOfOrigin, &People[idCounter].num_siblings, &People[idCounter].parentsAges[0], &People[idCounter].parentsAges[1]);
}
else // 7 whitespaces, thus 8 token in the string
{
sscanf(line, "%32s %8s %16s %c %16s %d %f %f", People[idCounter].full_name, str, surname, People[idCounter].sex, People[idCounter].countryOfOrigin, &People[idCounter].num_siblings, &People[idCounter].parentsAges[0], &People[idCounter].parentsAges[1]);
// Separate name and first word of surname with a space
strcat(People[idCounter].full_name, " ");
strcat(People[idCounter].full_name, str);
}
// Separate name and surname with a space
strcat(People[idCounter].full_name, " ");
strcat(People[idCounter].full_name, surname);
People[idCounter].person_ID = idCounter;
printf("%d %s %s %s %d %f %f\n", People[idCounter].person_ID, People[idCounter].full_name, People[idCounter].sex, People[idCounter].countryOfOrigin, People[idCounter].num_siblings, People[idCounter].parentsAges[0], People[idCounter].parentsAges[1]);
idCounter++;
if(idCounter == P)
{
printf("Max number of people read, stop reading any more data.\n");
break;
}
};
fclose(file);
printf("Finished reading file.\n");
}
int main() {
viewAllPersonalInformation();
return 0;
}
ID | Name |Sex | Born In |Number of siblings |Mother's age |Father's Age
0 John O'Donnell F Ireland 3 32.500000 36.099998
1 Mary Mc Mahon M England 0 70.000000 75.000000
2 Peter Thompson F America 2 51.000000 60.000000
Finished reading file.