Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/57.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/arrays/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
c带结构的错误访问fscanf_C_Arrays_String_Struct_Typedef - Fatal编程技术网

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.