读取输入文件并按一种类型升序排序(C语言)。

3
我正在编写一个程序,它接受一个文件输入并将其保存到数组中。问题是我不确定是否应该使用2D数组。特别是我听说的!feof循环可能不是正确的做法。我还需要找出city_mpg和highway_mpg的平均值,然后将其添加到数组中作为另一列。添加列后,我需要按升序排序。如果它是一个1D数组,我该如何找到平均值并将其添加到另一列?如果是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;
    }

期望结果:

Make Model     year city mpg highway mpg average mpg
Jeep Wrangler  2016    17          21          19
Mercury Sable  2009    18          28          23
and so on...

2
创建一个最小,完整和可验证的示例时,通常有助于确保它实际上完整和可验证的。换句话说,它可以编译和运行。除非你正在询问构建或运行时错误。除了期望的输出,还包括实际输出。 - Some programmer dude
3
请阅读《为什么“while(!feof(file))”总是错误的?》(https://dev59.com/jG035IYBdhLWcg3wbPU5)。 - Some programmer dude
1
在您自己的代码中,每种类型的汽车都由类型为“struct car”的对象表示。 您需要容纳多个这样的对象; 您似乎已经通过“struct car”的数组来解决这个问题(这很好)。 为什么您需要一个二维数组呢? - John Bollinger
1
你的代码中没有2D数组,它无法编译,并且不清楚你想从我们这里得到什么。请参见[ask]。 - too honest for this site
3
你可以像这样避免使用 feofwhile(fscanf(file, "%s %s %d %d %d", ...) == 5) {...},而且你必须始终检查 scanf 系列调用的返回值。 - Weather Vane
显示剩余2条评论
2个回答

2

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

#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

1
@user5468794 我稍微补充了一下注释。我建议你阅读 qsort 的 man 手册以获取更详细的描述。 - Weather Vane
最后还有一件可能有帮助的事情,如果它是从文件中读入程序的,那么你如何将输出写入到指定的文件中? - I'm here for Winter Hats
1
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Weather Vane
1
我通过进一步限制制造商和型号输入长度来修复了该错误。请注意,制造商和型号也必须是单词名称,因为“%s”会在第一个空格处停止。因此,“阿斯顿马丁”这样的制造商将无法使用。 - Weather Vane
我想保持文件打开状态,然后将 printf() 替换为 fprintf(file2,...),最后关闭两个文件,对吗? - I'm here for Winter Hats
1
为什么要把第一个文件指针留开呢?直接使用同一个文件指针即可。 - Weather Vane

1

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

第一个是 - “我需要使用feof吗?有没有替代方法?”

第二个是 - “在这种情况下,2D数组有什么实际用途?”

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

这种情况的一个很好的替代方法是读取并始终检查是否还有更多内容可读或已到达结尾,并且要始终“读取和检查”。

例如,在使用getc时,始终检查结果是否不是-1。这也可以处理其他情况,通常可以通过msdn来检查内置返回值。

对于第二个问题,在这种情况下不需要使用2D数组。您的汽车结构建得干净、动态和易读,因此您可以在之后更改表格而不需要在2D数组上创建另一列,因为这经常会导致混淆和动态变化较少。例如,如果突然想要在各种不同情况下再添加5列(MAX、MIN、AVG等等),这可能会显得有点繁琐。将数据值作为要粘贴出来的值,而不是用于保存程序中所有元数据的数据结构。这也将帮助您进行选择排序,因为这样您就不需要参考2D数组来对数据进行排序,而是直接使用对象本身。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接