Reputation: 57
I have learned about File processing in C programming recently. And I was given homework which call me to read data on a .txt files and printf out the data
The problem I'm facing with is my output appear random alien word*(smth like this ╝ c 0.00 6?φ↨ê■` 0.00)* when i enter my selection.BUT I think I code it properly (fopen and fclose the files, read the files with fread) and I just don't get it why my programm come into an error. I spend almost 3 days on youtube and google everything but I still failed on it and it almost reach the due date.
can someone please help me? Rlly thank you. also if you're free, please show me a correct code of this program so that I could make it as a reference. If you're not free its okay then :D //Is my system flow correct , if i wanna read the files, and printf specific line from the files at certain condition. ( e.g. defining struct > open > if-else statement > do -while loop >end) ? or we have other flowchart which is more smooth
//is it possible that i read all lines of the files, but I only printf out one single specific line?if yes, how can we do this?
Here is the question car.txt file shows variety of car maker, model, color and price. Design a program that read car maker, model, color and price from car.txt. List down the price options of for user to select from. The program will be able to display to the screen of particular car maker, model, color and price based on price range selection.
below is the .txt file
Toyota Altis Silver 120000.00
Toyota Vios Black 90000.00
Honda Accord Black 152000.00
Honda Civic Silver 118000.00
Nissan Cefiro Black 151000.00
Nissan Sylphy Silver 121000.00
Proton Perdana Black 110000.00
Proton Waja Blue 70000.00
//this was my code//
#include <stdio.h>
struct CarType
{
char carmaker[10],model[10],colour[10];
float price;
};
int main ()
{
char option;
FILE *fp;
struct CarType car[8];
if((fp = fopen("carM.txt", "r")) == NULL)
printf("file cant be open");
else
{
while (!feof(fp))
{
fread(&car[8], sizeof(struct CarType), 8, fp);
printf("\nChoose your option");
printf("\n1-List car price equal or above RM120,000");
printf("\n2-List car price RM120,000-RM149,999");
printf("\n3-List car price RM50,000-RM119,999");
printf("\n4-End program");
printf("\n>> ");
do
{
scanf("%c",&option);
if (option == '1')
{
printf("\nCar price equal or above RM120,000");
printf("\n %s %s %s %.2f",car[0].carmaker, car[0].model, car[0].colour, car[0].price);
printf("\n %s %s %s %.2f",car[2].carmaker, car[2].model, car[2].colour, car[2].price);
printf("\n %s %s %s %.2f",car[4].carmaker, car[4].model, car[4].colour, car[4].price);
printf("\n %s %s %s %.2f",car[5].carmaker, car[5].model, car[5].colour, car[5].price);
break;
}
if (option == '2')
{
printf("\nCar price RM120,000-RM149,999");
printf("\n %s %s %s %.2f",car[0].carmaker, car[0].model, car[0].colour, car[0].price);
printf("\n %s %s %s %.2f",car[5].carmaker, car[5].model, car[5].colour, car[5].price);
break;
}
if (option == '3')
{
printf("\nCar price RM50,000-RM119,999");
printf("\n %s %s %s %.2f",car[1].carmaker, car[1].model, car[1].colour, car[1].price);
printf("\n %s %s %s %.2f",car[3].carmaker, car[3].model, car[3].colour, car[3].price);
printf("\n %s %s %s %.2f",car[6].carmaker, car[6].model, car[6].colour, car[6].price);
printf("\n %s %s %s %.2f",car[7].carmaker, car[7].model, car[7].colour, car[7].price);
break;
}
if (option == '4')
{
printf("\nEnd pf Program");
}
else
{
printf("Error input");
}
}while(option!= '4');
}fclose (fp);
}
return 0;
}
Upvotes: 0
Views: 865
Reputation: 57
owwwwwwwwwwwwwwwww yeahhhhhhhhhhhhh thx everyone, My problem was solved. and here is my code
#include <stdio.h>
#include <stdlib.h>
struct carType
{
char maker[10],model[10],colour[10];
float price;
}carType;
int main()
{
int n;
int option;
FILE *fp;
struct carType car[8];
if((fp = fopen("car.txt", "r")) == NULL)
{
printf("File can't be opened.");
}
else
{
for(n=0;n<8;n++)
{
fscanf(fp,"%s %s %s %f",car[n].maker,car[n].model,car[n].colour,&car[n].price);
}
printf("Choose your option\n");
printf("1-List car price equal or above RM120,000\n");
printf("2-List car price RM120,000 - RM149,999\n");
printf("3-List car price RM50,000 - RM119,999\n");
printf("4-End program\n");
printf("?");
while (!feof(fp))
{
do
{
scanf("%d",&option);
if (option == 1)
{
printf("\nCar price equal or above RM120,000");
for(n=0;n<8;n++)
{
if(car[n].price>=120000)
{
printf("\n%-10s %-10s %-10s %.0f",car[n].maker, car[n].model, car[n].colour, car[n].price);
}
}
printf("\n\n");
break;
}
else if (option == 2)
{
printf("\nCar price RM120,000 - RM149,999");
for(n=0;n<8;n++)
{
if(car[n].price>=120000 && car[n].price<=149999)
{
printf("\n%-10s %-10s %-10s %.0f",car[n].maker, car[n].model, car[n].colour, car[n].price);
}
}
printf("\n\n");
break;
}
else if (option == 3)
{
printf("\nCar price RM50,000 - RM119,999");
for(n=0;n<8;n++)
{
if(car[n].price>=50000 && car[n].price<=119999)
{
printf("\n%-10s %-10s %-10s %.0f",car[n].maker, car[n].model, car[n].colour, car[n].price);
}
}
printf("\n\n");
break;
}
else if (option == 4)
{
printf("End of Program\n");
return 0;
}
else
{
printf("Error input...");
}
}while(option!= 4);
}fclose (fp);
}
return 0;
}
:D
Upvotes: 1
Reputation: 84579
No wonder you get strange input. You are attempting a binary read of text from a file into a struct
-- that won't work. Instead, you need to read a entire line of data with fgets()
or POSIX getline()
and then separate (parse) the needed values from the filled array with sscanf()
. While your member array size of 10
will work, buy yourself a little more room. I would use 16
at a minimum.
When you declare your struct
, you can add a typedef
an avoid having to write struct cartype
each time a type is needed, writing simply cartype
instead. you could do something similar to:
#include <stdio.h>
#define MAXC 16 /* if you need a constant, #define one (or more) */
#define LINE 1024
typedef struct { /* a typedef allows you to refer to the type without struct */
char carmaker[MAXC],
model[MAXC],
colour[MAXC];
double price;
} cartype;
In main()
, you need your array of struct, a counter and a buffer (character array) to hold each line read from your data file.
int main (int argc, char **argv) {
char buf[LINE]; /* buffer to hold line */
size_t n = 0; /* counter */
cartype car[MAXC] = {{.carmaker = ""}}; /* array of cartype (MAXC of them) */
(note: the declaration of int main (int argc, char **argv)
. Do not hardcode filenames in your code, instead pass the filename to read as an argument to your program. You should not have to recompile your code just to read from a different file)
You can read from the filename provide as the 1st argument to your program on the command line (or read from stdin
by default if no argument is given) with:
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
Above, a simple ternary is used to open the file given on the command line, or assign stdin
to fp
by default if no argument is given. A ternary is just a shorthand if .. else ..
statement with the form test ? if_true : if_false
. Where the test can be any conditional, and if the condition tests true
the if_true
part of the statement is used, otherwise the if_false
part is used.
Reading each line from the file and separating the values into your stuct
members couldn't be easier. You just read a line with fgets()
and then parse the values with sscanf()
validating the return of sscanf()
to confirm all values were successfully parsed from the line. On success, you just update your counter. You use the counter as a test condition in the while()
loop to ensure you do not attempt to store more values than your array can hold, e.g.
/* while array not full, read line of input */
while (n < MAXC && fgets (buf, LINE, fp)) { /* parse values with sscanf() */
if (sscanf (buf, "%15s %15s %15s %lf", /* always use field-width */
car[n].carmaker, car[n].model,
car[n].colour, &car[n].price) == 4) { /* validate 4 conversions */
n++; /* increment counter */
}
}
(note: when using any scanf()
family of functions for string input, you must use the field-width modifier to ensure you do not attempt to store more characters to an array than it can hold. Without the field-width modifier, scanf()/sscanf()
are no better than gets()
See: Why gets() is so dangerous it should never be used!)
That's it. All you need to do is close your input file and output your values, e.g.
if (fp != stdin) /* close file if not stdin */
fclose (fp);
for (size_t i = 0; i < n; i++)
printf ("car[%2zu] : %-16s %-16s %-16s %10.2f\n",
i, car[i].carmaker, car[i].model, car[i].colour, car[i].price);
}
Putting it altogether, you would have:
#include <stdio.h>
#define MAXC 16 /* if you need a constant, #define one (or more) */
#define LINE 1024
typedef struct { /* a typedef allows you to refer to the type without struct */
char carmaker[MAXC],
model[MAXC],
colour[MAXC];
double price;
} cartype;
int main (int argc, char **argv) {
char buf[LINE]; /* buffer to hold line */
size_t n = 0; /* counter */
cartype car[MAXC] = {{.carmaker = ""}}; /* array of cartype (MAXC of them) */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
/* while array not full, read line of input */
while (n < MAXC && fgets (buf, LINE, fp)) { /* parse values with sscanf() */
if (sscanf (buf, "%15s %15s %15s %lf", /* always use field-width */
car[n].carmaker, car[n].model,
car[n].colour, &car[n].price) == 4) { /* validate 4 conversions */
n++; /* increment counter */
}
}
if (fp != stdin) /* close file if not stdin */
fclose (fp);
for (size_t i = 0; i < n; i++)
printf ("car[%2zu] : %-16s %-16s %-16s %10.2f\n",
i, car[i].carmaker, car[i].model, car[i].colour, car[i].price);
}
Example Use/Output
With your sample input int the file dat/imports.txt
, you would pass the filename on the command line as the first argument, e.g.
$ ./bin/import_cars dat/imports.txt
car[ 0] : Toyota Altis Silver 120000.00
car[ 1] : Toyota Vios Black 90000.00
car[ 2] : Honda Accord Black 152000.00
car[ 3] : Honda Civic Silver 118000.00
car[ 4] : Nissan Cefiro Black 151000.00
car[ 5] : Nissan Sylphy Silver 121000.00
car[ 6] : Proton Perdana Black 110000.00
car[ 7] : Proton Waja Blue 70000.00
Look things over and let me know if you have further questions.
Upvotes: 2
Reputation: 78
This is a logical error, which just so happened to have altered the rest of your program.
fread(&car[8], sizeof(struct CarType), 8, fp);
Firstly, it is a buffer overflow. It writes memory starting at the end of the allocated buffer. You were probably thinking of this:
fread(car, sizeof(struct CarType), 8, fp);
Secondly, you are assuming each line is exactly the size of your struct (34 bytes). So, I would avoid using fread in this case, since you don't know the size of each line.
I recommend using this instead:
int fscanf(FILE *stream, const char *format, ...);
Example:
FILE* txtFile = fdopen("test.txt","r");
char a[10],b[10];
fscanf(txtFile, "%s %s\n", a, b);
To summarize, you should read one line at a time with fscanf
and once you are finished parsing all the data, then you should loop through all the cars to print out all the cars that satisfy the correct pricing ranges.
Suggestion: It might be helpful to create a print function for your struct. Something with this kind of prototype:
void printCar(CarType* car);
Also, man
pages are your friend. If you want more info on fscanf
do man fscanf
in your terminal or look up fscanf
man pages on Google.
Upvotes: 2
Reputation: 6669
If you created the file using NotePad, or any text editor for that matter, you need make sure it saved the text as ASCII
or UTF-8 no-BOM
. Otherwise, you'll have to deal with code point conversions, as the codes for storing text vary widely. See Wikipedia Character encoding, the history is tightly entangled with how C processes strings of text.
Your text appears to be what we call a space delimited file. That means each line is a record and each field in the record is delimited by whitespace. Your struct however is an abstraction over physical memory that defines the fields and their types. You need to read the text file and convert each record into a struct.
Read up on the following:
You have options. You can read each line of the file into your struct using fscanf
, or read each line into a string buffer using fgets
and then use strtok
to iterate over each token in the buffer and either strcpy
, in the case of the string fields, and strtof
for the float.
You'll find lots of examples of how others have solved similar problems in these search results: https://stackoverflow.com/search?q=%5Bc%5D+convert+string+to+struct%3F
Since this is a homework assignment, I won't just hand you code. Go study, pick a path and start writing code. As soon as you run into a problem, do a quick search here for any possible answers, and start a new question if you don't find the answer.
Upvotes: 1