C读取输入文件并对一种类型进行升序排序
我正在编写一个程序,它接受文件输入并将其保存到数组中。问题是我不完全确定是否应该使用2d数组。特别是我听到的while循环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
!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