Ok, continuing from the comment, and just as laid out in the comment, the first thing you need to do with any data handling problem is to read all of your data into a form that allows you to work with it as needed. Here you have varying types of data that all make up a unit (or car in this case). Whenever you have differing types of data you have to handle as a unit, you should be thinking struct. Here a struct car
makes sense:
typedef struct { /* struct for cars */
int id;
char color[MAXN];
char manf[MAXN];
int year;
} car;
You can now read each line in your data file into an array or struct car
and have all data readily available for the rest of your code.
When ever you have lines of data, you should be thinking line-oriented input, e.g. fgets
or getline
. While you could use the scanf
family here, you are better served to read a line at a time and parse your data from each line read. Also, it is often better to parse the data into temporary variables, and then if you validate you have all accounted for, then, and only then, copy your temporary variables to your struct and increment your index. For example you could do the following:
/* constants for max name, chars & structs */
enum { MAXN = 16, MAXC = 64, MAXS = 128 };
...
car cars[MAXS] = {{ .id = 0 }}; /* array of struct car */
char buf[MAXC] = "";
size_t i, j, idx = 0, nc = 0, nm = 0;
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
...
while (idx < MAXS && fgets (buf, MAXC, fp)) { /* read each line */
int tid, tyr; /* temp variables */
char tcol[MAXC] = "", tman[MAXC] = "";
/* separate into temp variables, validate, copy to struct */
if (sscanf (buf, " %d %s %s %d%*c", &tid, tcol, tman, &tyr) == 4) {
cars[idx].id = tid;
snprintf (cars[idx].color, MAXN, "%s", tcol);
snprintf (cars[idx].manf, MAXN, "%s", tman);
cars[idx++].year = tyr;
}
}
Now you have all your data in your array of struct, and the only thing left is to determine the unique colors and manufacturers you have within your data. You do not need to re-copy your data to another array, you can simply use an array of pointers to point to the unique values within your array of structs. You can do that as follows:
char *colors[MAXN] = {NULL}, *manfs[MAXN] = {NULL}; /* pointer arrays */
...
for (i = 0; i < idx; i++) { /* find unique colors */
if (!nc) { colors[nc++] = cars[i].color; continue; }
for (j = 0; j < nc; j++)
if (!strcmp( colors[j], cars[i].color)) goto cdup;
colors[nc++] = cars[i].color;
cdup:;
}
(where nc
is just your counter for the number of unique colors)
You can do the same thing for your manufacturers.
Putting all the pieces together, (and noting all of your manufacturers have unique fist letters to begin with) you could do something like this:
#include <stdio.h>
#include <string.h>
/* constants for max name, chars & structs */
enum { MAXN = 16, MAXC = 64, MAXS = 128 };
typedef struct { /* struct for cars */
int id;
char color[MAXN];
char manf[MAXN];
int year;
} car;
int main (int argc, char **argv) {
car cars[MAXS] = {{ .id = 0 }}; /* array of struct car */
char *colors[MAXN] = {NULL}, *manfs[MAXN] = {NULL}; /* pointer arrays */
char buf[MAXC] = "";
size_t i, j, idx = 0, nc = 0, nm = 0;
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
while (idx < MAXS && fgets (buf, MAXC, fp)) { /* read each line */
int tid, tyr; /* temp variables */
char tcol[MAXC] = "", tman[MAXC] = "";
/* separate into temp variables, validate, copy to struct */
if (sscanf (buf, " %d %s %s %d%*c", &tid, tcol, tman, &tyr) == 4) {
cars[idx].id = tid;
snprintf (cars[idx].color, MAXN, "%s", tcol);
snprintf (cars[idx].manf, MAXN, "%s", tman);
cars[idx++].year = tyr;
}
}
if (fp != stdin) fclose (fp);
for (i = 0; i < idx; i++) /* output all data */
printf ("%4d %-6s %-10s %d\n", cars[i].id, cars[i].color,
cars[i].manf, cars[i].year);
putchar ('\n');
for (i = 0; i < idx; i++) { /* find unique colors */
if (!nc) { colors[nc++] = cars[i].color; continue; }
for (j = 0; j < nc; j++)
if (!strcmp( colors[j], cars[i].color)) goto cdup;
colors[nc++] = cars[i].color;
cdup:;
}
for (i = 0; i < idx; i++) { /* find unique manufacturers */
if (!nm) { manfs[nm++] = cars[i].manf; continue; }
for (j = 0; j < nm; j++)
if (*manfs[j] == *cars[i].manf) goto mdup;
manfs[nm++] = cars[i].manf;
mdup:;
}
/* output unique colors & unique manufacturers */
for (i = 0; i < nc; i++) printf ("%s\n", colors[i]);
putchar ('\n');
for (i = 0; i < nm; i++) printf ("%s\n", manfs[i]);
putchar ('\n');
return 0;
}
note: if your did not have all unique first letters for manufacturers, you would need a full strcmp
when testing like with colors, instead of just comparing the first characters.
Example Input
$ cat dat/cars.txt
4132 RED ALFA-ROMEO 2005
5230 BLUE FIAT 2004
4321 WHITE NISSAN 2000
5233 BLUE BMW 2001
7300 YELLOW CITROEN 1999
1232 BLUE PEUGEOT 1990
9102 RED VW 1995
1998 YELLOW ALFA-ROMEO 2004
5333 WHITE VW 1999
6434 BLUE NISSAN 2000
8823 BLACK MERCEDES 2003
4556 BLACK SEAT 1997
1554 RED MERCEDES 2001
6903 YELLOW NISSAN 2000
7093 BLACK FIAT 1978
1023 WHITE VW 1998
3422 BLUE SEAT 2005
3555 RED BMW 2004
6770 YELLOW SEAT 2002
Example Use/Output
$ ./bin/cars <dat/cars.txt
4132 RED ALFA-ROMEO 2005
5230 BLUE FIAT 2004
4321 WHITE NISSAN 2000
5233 BLUE BMW 2001
7300 YELLOW CITROEN 1999
1232 BLUE PEUGEOT 1990
9102 RED VW 1995
1998 YELLOW ALFA-ROMEO 2004
5333 WHITE VW 1999
6434 BLUE NISSAN 2000
8823 BLACK MERCEDES 2003
4556 BLACK SEAT 1997
1554 RED MERCEDES 2001
6903 YELLOW NISSAN 2000
7093 BLACK FIAT 1978
1023 WHITE VW 1998
3422 BLUE SEAT 2005
3555 RED BMW 2004
6770 YELLOW SEAT 2002
RED
BLUE
WHITE
YELLOW
BLACK
ALFA-ROMEO
FIAT
NISSAN
BMW
CITROEN
PEUGEOT
VW
MERCEDES
SEAT
Note: this is just to get you started. You still have to use the data however the rest of your assignment calls for, but here is one approach to getting it in manageable form. Let me know if you have any questions.
Simplified Version Print Non-Duplicate Color & Manufacturer
I thought about the difficulty you were having and the basic problem in knowing how much detail you need stems from not knowing how you will ultimately use your data. Initially, you needed a way to identify the unique colors and manufacturers within your data file. The first code separates all the data into unique collections of colors and manufacturers, but leaves it up to you to implement what you needed done. However, if you simply need to output the list of cars with unique colors by unique manufacturers, then you don’t need to store an array of structs at all, you simply print those vehicles with unique color/manufacturers until duplicates are reached. That can be done in a simplified manner as follows:
#include <stdio.h>
#include <string.h>
/* constants for max name, chars & structs */
enum { MAXN = 16, MAXC = 64 };
int main (int argc, char **argv) {
char colors[MAXN][MAXN] = {{0}}, manfs[MAXN][MAXN] = {{0}};
char buf[MAXC] = "";
size_t i, nc = 0, nm = 0;
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
while (fgets (buf, MAXC, fp)) { /* read each line */
int tid, tyr; /* temp variables */
char tcol[MAXC] = "", tman[MAXC] = "";
/* separate into temp variables, validate, copy to struct */
if (sscanf (buf, " %d %s %s %d%*c", &tid, tcol, tman, &tyr) == 4) {
if (!nc) /* 1st color - add it */
strcpy (colors[nc++], tcol);
else {
for (i = 0; i < nc; i++) /* compare against stored colors */
if (!strcmp (colors[i], tcol)) /* duplicate */
goto dupe; /* skip it */
strcpy (colors[nc++], tcol); /* add it */
}
if (!nm) /* do the same for manufacturers */
strcpy (manfs[nm++], tman);
else {
for (i = 0; i < nm; i++)
if (!strcmp (manfs[i], tman))
goto dupe;
strcpy (manfs[nm++], tman);
} /* if not a duplicate, print the vehicle */
printf ("%4d %-6s %-10s %d\n", tid, tcol, tman, tyr);
dupe:;
}
}
if (fp != stdin) fclose (fp);
return 0;
}
Example Use/Output
$ ./bin/cars2 <dat/cars.txt
4132 RED ALFA-ROMEO 2005
5230 BLUE FIAT 2004
4321 WHITE NISSAN 2000
7300 YELLOW CITROEN 1999
8823 BLACK MERCEDES 2003
Look over what is done. Each line is read and the value separated. If the color or manufacturer array is empty the first color/manufacturer is added. For all others, the current color or manufacturer is compared against all previous and if it is a duplicate, printing is skipped for that car. Let me know if you have further questions.
6
solved Printing part of a .txt file [closed]