C读取输入文件并对一种类型进行升序排序

C读取输入文件并对一种类型进行升序排序,c,arrays,sorting,file-io,C,Arrays,Sorting,File Io,我正在编写一个程序,它接受文件输入并将其保存到数组中。问题是我不完全确定是否应该使用2d数组。特别是我听到的while循环!feof可能不是解决问题的办法。我还需要找出城市和高速公路的平均值,然后将其作为另一列添加到数组中。添加列后,我需要对其进行升序排序。如果它是一个一维数组,我如何找到平均值,然后将其添加到另一列?如果它是2D,我不能只指定[1][4]和[1][5]并执行类似的操作,然后将其保存为[1][6]等等,还是应该使用Malloc 输入文件: Mercury Sable 2009 1

我正在编写一个程序,它接受文件输入并将其保存到数组中。问题是我不完全确定是否应该使用2d数组。特别是我听到的while循环
!feof
可能不是解决问题的办法。我还需要找出城市和高速公路的平均值,然后将其作为另一列添加到数组中。添加列后,我需要对其进行升序排序。如果它是一个一维数组,我如何找到平均值,然后将其添加到另一列?如果它是2D,我不能只指定
[1][4]
[1][5]
并执行类似的操作,然后将其保存为
[1][6]
等等,还是应该使用Malloc

输入文件:

Mercury Sable 2009 18 28
Jeep Wrangler 2016 17 21
Honda civic 2015 31 41
Toyota Corolla 2015 30 42
Toyta Prius 2010 51 48
Ford Escape 2013 23 33
Ford Fusion 2013 25 37
Acura MDX 2014 20 28
Lexus RX 2013 32 28
程序不完整:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_CARS 1000 //no more than 1000 cars
#define MAX_STR  30  //str wont be longer than 30

struct car {                       // declare my structure
    char *make;                    // pointers for char and declares my vars
    char *model;                  
    int manufacture_year;
    int city_mpg;
    int highway_mpg;
    int average_mpg;
};

//sorts array based on average mpg here

int main(void) { //main function
    int cars = 0;
    int c;
    struct car *data;
    char make[MAX_STR+1];  //char will be 30 + 1 for null char
    char model[MAX_STR+1];
    int year, city, highway; //declares ints
    FILE *file; //declares input file
    FILE *file2; //declares output file

    file = fopen("cars.txt", "r"); //opens car.txt as read
    if(file == NULL) { //if file is null
        printf("File error\n"); //throws error
        return 1;
    }

    data = malloc(MAX_CARS * sizeof(struct car)); //allocates memory for array by max cars for pointers
    if(data == NULL) {
        printf("Memory error\n"); //error if memory is a issue just incase mainly used for testing
        return 1;
    }

    while(fscanf(file, "%30s %30s %d %d %d", make, model, &year, &city, &highway) == 5) { //reads the data with a while loop
        if(cars >= MAX_CARS) { //just a check if file is more than 1k
            printf("Too many cars\n"); //outputs result if too many cars
            break;
        }
        data[cars].make = strdup(make);             // makes a copy of the strings
        data[cars].model = strdup(model);            
        data[cars].manufacture_year = year;         // copies the data so that it is headed properly
        data[cars].city_mpg = city;                 // copies the data so that it is headed properly
        data[cars].highway_mpg = highway;           // copies the data so that it is headed properly
        data[cars].average_mpg = (city + highway) / 2; //gets the average mpg
        cars++;                                     // loop counter
    }
    fclose(file); //closes file

    file2 = fopen("sorted_cars.txt", "a+"); //opens new file or creates one if there isnt one

    fprintf(file2,"Make Model           Year City mpg Highway mpg Average mpg\n"); //prints to new txt file
    for(c=0; c<cars; c++) {
        sprintf(model, "%s %s", data[c].make, data[c].model);    //sprintf sends formatted output to a string
        fprintf(file2,"%-20s %4d  %4d        %4d        %4d\n", model, data[c].manufacture_year,data[c].city_mpg, data[c].highway_mpg, data[c].average_mpg); //prints to oufile
    }

    // free the memory, It tries to allocate enough memory to hold the old string (plus a null character to mark the end of the string)
    while(--cars >= 0) {
        free(data[cars].model);     
        free(data[cars].make);       
    }
    free(data); //frees the array memory
    return 0;
    }

让我们将问题分为两个主要部分:

第一个问题是“我需要feof吗?有其他选择吗?”

第二个问题是“在这种情况下,2D阵列是否有实际用途?”

所以我要从第一个问题开始。 我不建议使用feof,因为feof测试的是文件结束指示符,而不是流本身。这意味着另一个函数实际上负责设置到达时的EOF,这是不好的

一个很好的替代方法是阅读并经常检查是否还有更多的内容要读,或者是否已经结束,而要做到这一点,你需要经常“阅读并检查”

例如,在使用getc时,始终检查结果是否不是-1。这也考虑了其他情况,通常msdn是检查构建返回值的方法


对于第二个问题,在本例中不需要二维数组。您的汽车结构是干净的、动态的和可读的,因此您可以在以后对表进行更改,而无需在二维数组上创建另一列,因为这通常会导致混乱和更少的动态更改。例如,如果您突然想要在各种不同的情况下(MAX、MIN、AVG等)再增加5列,这可能会显得有点累。将数据值设置为要粘贴的值,而不是保存程序上所有元的数据结构。这也将帮助您进行选择排序,因为这样您就不需要引用2d数组来对数据进行排序,而是对象本身。

我对您的代码进行了一些调整,并添加了一些注释。它使用一维数组

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_CARS 1000
#define MAX_STR  99

struct car {                       // array of cars appears later
    char *make;                    // pointer to string memory that will be allocated
    char *model;                   // ditto
    int manufacture_year;
    int city_mpg;
    int highway_mpg;
    int average_mpg;
};

int cmp(const void *a, const void *b) {
    // compare function for qsort
    // this is the user-supplied compare function always required by qsort
    // qsort does not know or care about the data type, only its dimensions
    // so I cast the void pointers to our local data type
    struct car *aa = (struct car*)a;
    struct car *bb = (struct car*)b;
    if(aa->average_mpg > bb->average_mpg) return 1;
    if(aa->average_mpg < bb->average_mpg) return -1;
    return 0;
}

int main(void) {
    int cars = 0;
    int c;
    struct car *data;
    char make[MAX_STR+1];
    char model[MAX_STR+1];
    int year, city, highway;
    FILE *file;

    // set up
    file = fopen("cars.txt", "r");
    if(file == NULL) {
        printf("File error\n");                     // finish messages with a newline
        return 1;
    }
    // allocate dynamic memory for the array, for maximum cars specified
    // the argument is the total memory requirement
    // could have been a global array of struct but it's bad practice
    data = malloc(MAX_CARS * sizeof(struct car));
    if(data == NULL) {
        printf("Memory error\n");
        return 1;
    }

    // read the data, controlling the loop with fscanf return value
    // feof is commonly, but incorrectly used, and since it is essential to check the 
    // return value from fscanf, this kills two birds with one stone
    while(fscanf(file, "%49s %49s %d %d %d", make, model, &year, &city, &highway) == 5) {
        if(cars >= MAX_CARS) {
            printf("Too many cars\n");
            break;
        }
        data[cars].make = strdup(make);             // make a copy of the strings
        data[cars].model = strdup(model);
        data[cars].manufacture_year = year;         // copy the data
        data[cars].city_mpg = city;
        data[cars].highway_mpg = highway;
        data[cars].average_mpg = (city + highway) / 2;
        cars++;                                     // track the number of records
    }
    fclose(file);

    // sort the records, qsort needs to know the width of each element, 
    // and how many many, and you tell it your own comparison callback function
    qsort(data, cars, sizeof *data, cmp);

    // print the data
    printf("Make Model           Year City mpg Highway mpg Average mpg\n");
    for(c=0; c<cars; c++) {
        sprintf(model, "%s %s", data[c].make, data[c].model);   // to make alignment easy
        printf("%-20s %4d  %4d        %4d        %4d\n", model, data[c].manufacture_year,
                    data[c].city_mpg, data[c].highway_mpg, data[c].average_mpg);
    }

    // free the memory, note that strdup allocated memory secretly
    while(--cars >= 0) {
        free(data[cars].model);      // it was acquired by strdup
        free(data[cars].make);       // so was this
    }
    free(data);                      // now free the array memory we got ourselves
    return 0;
    }

创建文档时,它通常有助于确保文档实际上是完整的和可验证的。换句话说,它编译并运行。除非您询问生成或运行时错误。除预期输出外,还包括实际输出。另外,请阅读。在您自己的代码中,每种类型的汽车都由类型为
struct car
的对象表示。您需要容纳多个这样的对象;您似乎已经决定这样做的方式是通过一个
struct car
数组(这很好)。你为什么需要一个2D数组来做这个呢?对于一个简单的答案来说,有太多的事情要做了。例如,make和model成员应该是字符数组(或指针)。您可以避免像这样使用
feof
while(fscanf(file,“%s%s%d%d%d”,…)==5{…}
,并且您必须始终检查
scanf
家族调用的返回值。@user5468794我已经对注释进行了一些充实。我建议您阅读
qsort
的手册页以获得更全面的描述。最后一件可能有用的事情是,如果它从文件读入程序,您将如何将输出写入指定的文件?打开文本文件进行写入,并在我使用
printf
的地方使用
fprintf(file,…)
。代码中实际上存在一个(不明显的)错误-我为输出生成了一个汽车类型字符串,
model[]
如果汽车详细信息连接到99个字符以上,可能没有足够的空间。不太可能,但仍然是一个潜在的漏洞。我已经通过进一步限制make&model输入长度修复了这个错误。请注意,make和model也必须是单字名称,因为
%s
在第一个空格处停止。“阿斯顿·马丁”这个牌子不起作用了。为什么要把第一个打开呢?只需使用相同的文件指针。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_CARS 1000
#define MAX_STR  99

struct car {                       // array of cars appears later
    char *make;                    // pointer to string memory that will be allocated
    char *model;                   // ditto
    int manufacture_year;
    int city_mpg;
    int highway_mpg;
    int average_mpg;
};

int cmp(const void *a, const void *b) {
    // compare function for qsort
    // this is the user-supplied compare function always required by qsort
    // qsort does not know or care about the data type, only its dimensions
    // so I cast the void pointers to our local data type
    struct car *aa = (struct car*)a;
    struct car *bb = (struct car*)b;
    if(aa->average_mpg > bb->average_mpg) return 1;
    if(aa->average_mpg < bb->average_mpg) return -1;
    return 0;
}

int main(void) {
    int cars = 0;
    int c;
    struct car *data;
    char make[MAX_STR+1];
    char model[MAX_STR+1];
    int year, city, highway;
    FILE *file;

    // set up
    file = fopen("cars.txt", "r");
    if(file == NULL) {
        printf("File error\n");                     // finish messages with a newline
        return 1;
    }
    // allocate dynamic memory for the array, for maximum cars specified
    // the argument is the total memory requirement
    // could have been a global array of struct but it's bad practice
    data = malloc(MAX_CARS * sizeof(struct car));
    if(data == NULL) {
        printf("Memory error\n");
        return 1;
    }

    // read the data, controlling the loop with fscanf return value
    // feof is commonly, but incorrectly used, and since it is essential to check the 
    // return value from fscanf, this kills two birds with one stone
    while(fscanf(file, "%49s %49s %d %d %d", make, model, &year, &city, &highway) == 5) {
        if(cars >= MAX_CARS) {
            printf("Too many cars\n");
            break;
        }
        data[cars].make = strdup(make);             // make a copy of the strings
        data[cars].model = strdup(model);
        data[cars].manufacture_year = year;         // copy the data
        data[cars].city_mpg = city;
        data[cars].highway_mpg = highway;
        data[cars].average_mpg = (city + highway) / 2;
        cars++;                                     // track the number of records
    }
    fclose(file);

    // sort the records, qsort needs to know the width of each element, 
    // and how many many, and you tell it your own comparison callback function
    qsort(data, cars, sizeof *data, cmp);

    // print the data
    printf("Make Model           Year City mpg Highway mpg Average mpg\n");
    for(c=0; c<cars; c++) {
        sprintf(model, "%s %s", data[c].make, data[c].model);   // to make alignment easy
        printf("%-20s %4d  %4d        %4d        %4d\n", model, data[c].manufacture_year,
                    data[c].city_mpg, data[c].highway_mpg, data[c].average_mpg);
    }

    // free the memory, note that strdup allocated memory secretly
    while(--cars >= 0) {
        free(data[cars].model);      // it was acquired by strdup
        free(data[cars].make);       // so was this
    }
    free(data);                      // now free the array memory we got ourselves
    return 0;
    }
Make Model           Year City mpg Highway mpg Average mpg
Jeep Wrangler        2016    17          21          19
Mercury Sable        2009    18          28          23
Acura MDX            2014    20          28          24
Ford Escape          2013    23          33          28
Lexus RX             2013    32          28          30
Ford Fusion          2013    25          37          31
Honda civic          2015    31          41          36
Toyota Corolla       2015    30          42          36
Toyta Prius          2010    51          48          49