I'm having some trouble in extracting data from a file, I don't understand what's wrong, it works fine for single digit integers, but when the input is double digit, it doesn't scan the integers. Everything else is working.
Suppose the input is:
abc de,1,2,y
then the output is:
"abc de" 1 2 y
but when the input is
cde abc,21.31,y
the scan fails on %d:
fin1 = fscanf(fp1, "%20[^,]%*c%d %*c %d%*c %c%*c", name1, &code1, &season, &relevant);
Help would be nice. Also some input as you asked:
Input:
The Universe,2,3,Y|Zoo,7,3,N|The Hobbit,10,2,Y|True Lies,12,25,N|Animals,22,2,Y| Euphoria,35,5,Y
Code:
FILE *fp1, *fserie;
char file1name[256], name1[21], relevant = 0, active = 0;
int code1, seasons, fin = 0,
while (1) {
puts("Enter First File Name:");
scanf("%s", file1name);
if (!(fp1 = fopen(file1name, "r")))
{
printf("error in opening file %s !!!\n", file1name);
continue;
} else break;
}
if (!(fserie = fopen("series.txt", "w")))
{
fclose(fp1);
exit(1);
}
do
{
fin1 = fscanf(fp1,"%20[^,]%*c %d%*c %d%*c %c%*c", name1, &code1, &season, &active1);
if (relevant == 'y')
fprintf(fserie,"%s,%d,%d\n", name1, code1, season);
} while (fin1 != EOF && fin2 != EOF);
The problem you are having is your format string and your failure to validate the read against all 4 successful conversions per iteration. For your format string you can use:
" %20[^,],%d,%d,%c|"
(the leading space is optional, but required if you have space following the '|' before the next name. Also note, if you have the potential for space surrounding the final active character, you can handle that by including a space in your format string to consume any-and-all whitespace, e.g. " %20[^,],%d,%d, %c |")
An example reading a line with multiple sets of movie information could be:
#include <stdio.h>
#define MAXN 21 /* if you need a constant, #define one (or more) */
int main (int argc, char **argv) {
char name[MAXN], active;
int code, seasons;
/* 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 (fscanf (fp, " %20[^,],%d,%d,%c|",
name, &code, &seasons, &active) == 4) {
printf ("\nname : %s\ncode : %d\nseason : %d\nactive : %c\n",
name, code, seasons, active);
}
if (fp != stdin) /* close file if not stdin */
fclose (fp);
}
Example Input File
$ cat dat/movies.txt
The Universe,2,3,Y|Zoo,7,3,N|The Hobbit,10,2,Y|True Lies,12,25,N|Animals,22,2,Y| Euphoria,35,5,Y
Example Use/Output
$ ./bin/readmovies dat/movies.txt
name : The Universe
code : 2
season : 3
active : Y
name : Zoo
code : 7
season : 3
active : N
name : The Hobbit
code : 10
season : 2
active : Y
name : True Lies
code : 12
season : 25
active : N
name : Animals
code : 22
season : 2
active : Y
name : Euphoria
code : 35
season : 5
active : Y
Look things over and let me know if you have further questions. There is more than one way to do this, but this is the closest to your original.
Related
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;
}
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.
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.
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:
fgets
fscanf
strtof
strtok
strcpy
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.
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
I'm a little desperate because I don't know how to create a program that only reads some data (words or numbers) from an input file and then with this data writes another file but putting them in a tabulated order ...
I don't know how to make the program look in the line of the input file for example "number of sequences: 2" and to make that it takes only the data "2" to be stored in the new file ...
Please help me
I'm just starting
Thank you all
The issue you are having is not with the loop, and not with the eof.
The real issue is you have incorrect parsing logic.
Your input file is not uniformed:
Different session lines have different "MODE" in them
Number of blank lines varies from group to group
Blank lines may actually contain any number of space characters
"Number of sequences" line appears in different places in different groups
To parse such a file you need a more flexible logic that will check each input line, collect all the data needed to build an output line, and only then print it to the output file.
To do this, you can use one loop reading only one line at a time, and then testing its contents using the strncmp function.
Once you identified the type of data the line contains, save it to a variable using sscanf function.
Here is the code that will do the job:
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv) {
FILE *file_in, *file_out;
char line[200];
/* intialize these just in case we want to validate the input file */
int current_session = 0;
int current_sequences = 0;
int current_registration = 0;
/* these arrays can probably be smaller */
char chars_given[200] = { 0 };
char chars_recognized[200] = { 0 };
file_in = fopen("summary.txt", "r");
if (file_in == NULL) {
perror("Error opening input file");
return 1;
}
file_out = fopen("ordinated.txt", "w");
if (file_out == NULL) {
perror("Error opening output file");
return -1;
}
while (fgets(line, 200, file_in) != NULL) {
/* check if this is start of session using safe string comparison */
if (strncmp(line, "session", strlen("session")) == 0) {
sscanf(line, "session %d", ¤t_session);
} else if (strncmp(line, "number of sequences", strlen("number of sequences")) == 0) {
sscanf(line, "number of sequences: %d", ¤t_sequences);
} else if (strncmp(line, "registration", strlen("registration")) == 0) {
sscanf(line, "registration %d", ¤t_registration);
} else if (strncmp(line, "characters given", strlen("characters given")) == 0) {
sscanf(line, "characters given: %s", chars_given);
} else if (strncmp(line, "characters recognized", strlen("characters recognized")) == 0) {
sscanf(line, "characters recognized: %s", chars_recognized);
} else {
/* This is a line with no information (blank or separator).
Time to print results we collected, and reset the variables
for the next set of results. */
/* check we have enough information to output a line */
if (current_session > 0 && current_sequences > 0 &&
current_registration > 0 && strlen(chars_given) > 0) {
/* check if anything was recognized */
if (strlen(chars_recognized) > 0) {
fprintf(file_out, "%d %d %d %s %s\n", current_session, current_registration,
current_sequences, chars_given, chars_recognized);
} else { /* one less parameter to output if nothing was recognized */
fprintf(file_out, "%d %d %d %s\n", current_session, current_registration,
current_sequences, chars_given);
}
/* Now reset for next time. If you don't do this, the output line will repeat */
current_registration = 0;
chars_given[0] = '\0';
chars_recognized[0] = '\0';
}
}
}
/* the last block may not be printed in the loop if there is no empty line after it */
if (current_session > 0 && current_sequences > 0 &&
current_registration > 0 && strlen(chars_given) > 0) {
/* check if anything was recognized */
if (strlen(chars_recognized) > 0) {
fprintf(file_out, "%d %d %d %s %s\n", current_session, current_registration,
current_sequences, chars_given, chars_recognized);
} else { /* one less parameter to output if nothing was recognized */
fprintf(file_out, "%d %d %d %s\n", current_session, current_registration,
current_sequences, chars_given);
}
}
fclose(file_in);
fclose(file_out);
return 0;
}
This code is a bit ugly, but I tried to keep it simple.
It can be cleaned up by using structures, some flags, and moving some of the code to separate functions.
Edit: this code omits sanity checks for simplicity, and assumes the input file is not corrupt, i.e. first non empty line is always session, lines contain all the information they should, etc.
The Text File was writable with 1 blank line after the struct.
Is similar to:
1 111 1 Peter
22 22 2 John Lays
3 3 3 Anne Belgs
The struct is:
struct estruturaCarro {
int id, potencia, avariado;
char name[11];
} carro;
I have write the data to the text File:
fprintf(fp, "%-2d %-3d %-1d %-10s \n\n", carro.id, carro.potencia, carro.avariado, carro.name);
I read the file data this way, but I'm sure it's not the best way:
while(true){
int result = fscanf(fp, "%d %d %d %10[^\n]", &carro.id, &carro.potencia, &carro.avariado, &carro.name);
if(result==4){
printf("%-2d %-3d %-1d % -s\n", carro.id, carro.potencia, carro.avariado, carro.name);
}else break;
}
How can I read the text file, saving the data struct, without reading the blank lines?
If I want to validate the values read (ID = xx, potencia = xxx, avariado = x, name = 10 characters), is it better, before or after filling the array of struct?The format of each line of the file is:xx xxx x aaaaaaaaaa (x = digits, a = characters)For example, if one of the lines is
9 123 4 Error 1st value
stop reading the file (informing the user), because the line should be:
9 123 4 Error 1st value (one space was missing after 9) - or because NAME has more than 10 characters.
You don't show how true is set in while(true), but it must be based on the return of fscanf in the way you are approaching it. You must test three cases:
fscanf return is EOF, read done, true should be set to FALSE to break loop;
fscanf return less than 4, matching or input failure, no guarantee this occurred due to an empty line; and
fscanf return is equal to 4, good input.
Unless you are checking all three, you cannot cover all cases for fscanf and further, if there is any additional or extraneous character in your input file, your formatted read will fail.
That is why a better option is to read each line into a buffer with fgets and then parse the information you need from the buffer itself with sscanf. This provides the benefit of allowing separate validation of (1) the read; and (2) the parse of information. Further, since you are consuming a line-at-a-time, there is no uncertainty on what remains in the input stream and a failure in parsing any one line does not prevent a successful read of the remainder.
A short implementation with fgets() and sscanf() reading each struct into an array-of-struct, you can do something similar to the following:
#include <stdio.h>
#define MAXN 11 /* if you need a constant, #define one (or more) */
#define MAXC 1024 /* (don't skimp on buffer size) */
typedef struct { /* use a simple typedef */
int id, potencia, avariado;
char name[MAXN];
} carro_t;
int main (int argc, char **argv) {
char buf[MAXC]; /* buffer to read line */
carro_t carro[MAXN] = {{ .id = 0 }}; /* array of struct */
size_t ndx = 0; /* array index */
/* 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 (ndx < MAXN && fgets (buf, MAXC, fp)) { /* read each line */
carro_t tmp = { .id = 0 }; /* temp struct */
if (*buf == '\n') /* if line empty */
continue; /* get next line */
if (sscanf (buf, "%d %d %d %10[^\n]", /* separate/validate */
&tmp.id, &tmp.potencia, &tmp.avariado, tmp.name) == 4)
carro[ndx++] = tmp; /* add to array of struct */
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
for (size_t i = 0; i < ndx; i++)
printf ("%3d %3d %3d %s\n",
carro[i].id, carro[i].potencia, carro[i].avariado, carro[i].name);
return 0;
}
(note: the filename is provide as the first argument to the program, or if no filename is provided, read from stdin by default)
While with your particular data file, there is no reason you cannot use fscanf, it is fragile and one character too many (like "Anne Belgss") will cause it to break. A fscanf implementation removing the true and simply looping as you have could be:
for (;;) {
carro_t tmp = { .id = 0 };
if (fscanf (fp, "%d %d %d %10[^\n]", /* separate/validate */
&tmp.id, &tmp.potencia, &tmp.avariado, tmp.name) == 4)
carro[ndx++] = tmp; /* add to array of struct */
else
break;
}
Either way, you will produce the same output with "this" input file, e.g.
Example Use/Output
$ ./bin/readwblanks ~/tmpd/file
1 111 1 Peter
22 22 2 John Lays
3 3 3 Anne Belgs
just use simple reading way is ok,like this.
while(fscanf(fp,"%d %d %d %s",&carro.id,&carro.potencia,&carro.avariado,carro.name)!=EOF)
printf("%d %d %d %s\n",carro.id,carro.potencia,carro.avariado,carro.name);
result is like this
enter image description here
So I have a file (that is an address book) that contains informations about people formatted this way
————————-CONTACT————————-
First Name : Tony
Last Name : Stark
Address : Malibu
Phone number : 10203044032
E-mail : tony.stark#jarvis.com
Company / Place of work : Stark Industries
————————-CONTACT————————-
I have this code :
#define MAX_VALUE_FOR_ARRAYS 1000
int main()
{
long pos = 0; /// this will store the position of the cursor
char address_book_content[MAX_VALUE_FOR_ARRAYS];
char contact_name[MAX_VALUE_FOR_ARRAYS];
char *string_exists = NULL;
File *show_address_book = NULL;
show_address_book = fopen("addressBook.txt", "r");
printf("Enter the name of the contact you want to search");
scanf("%s", &contact_name);
/** I want : when the user inputs a name, the program searches it in the file and if it’s found it prints the rest of the file starting from the line where that name is. So I tried the following **/
while ( (fgets(address_book_content, MAX_VALUE_FOR_ARRAYS, show_address_book) != NULL)
{
if ( (string_exists = strstr(address_book_content, contact_name) != NULL)
{
printf("%s", address_book_content);
pos = ftell(show_address_book);
fseek(show_address_book, 27, pos); /// 27 in just a random number, I just want it to change the position of the cursor in the file
printf("%s", address_book_content);
}
}
return 0;
}
When I input “Tony” for example it only displays :
First Name : Tony
I want it to display the whole information for the contact “Tony”
So if you could help me, thanks
in
printf(“%s”, address_book_content);
pos = ftell(show_address_book);
fseek(show_address_book, 27, pos); /// 27 in just a random number, I just want it to change the position of the cursor in the file
printf(“%s”, address_book_content);
the fseek doesn't change address_book_content so you write two times the same thing
you need to read in your file from the position you computed to be able to write what you read, modifying address_book_content is you still print it. Or just read & print the next lines after the one you read :
while ( (fgets(address_book_content, MAX_VALUE_FOR_ARRAYS, show_address_book) != NULL)
{
if ( (string_exists = strstr(address_book_content, contact_name) != NULL)
{
fputs(address_book_content, stdout); /* fputs rather printf to not write again a \n */
/* supposing it was the firstname there are 5 lines after to read and print */
fputs(fgets(address_book_content, MAX_VALUE_FOR_ARRAYS, show_address_book), stdout);
fputs(fgets(address_book_content, MAX_VALUE_FOR_ARRAYS, show_address_book), stdout);
fputs(fgets(address_book_content, MAX_VALUE_FOR_ARRAYS, show_address_book), stdout);
fputs(fgets(address_book_content, MAX_VALUE_FOR_ARRAYS, show_address_book), stdout);
fputs(fgets(address_book_content, MAX_VALUE_FOR_ARRAYS, show_address_book), stdout);
break;
}
}
Warning strstr is not the right way because it will catch name containing the expected one, for instance the name IamNotTonyAtAll match Tony using strstr
Of course this is quite very expensive to read each time the file to search for a name ...
A full proposal :
#include <stdio.h>
#include <string.h>
#define MAX_VALUE_FOR_ARRAYS 1000
int main()
{
FILE * fp = fopen("addressBook.txt", "r");
if (fp == NULL)
puts("cannot read addressBook.txt");
else {
/* use static vars to not take place into the stack */
static char contact_name[MAX_VALUE_FOR_ARRAYS];
printf("Enter the name of the contact you want to search : ");
if (scanf("%s", contact_name) != 1)
puts("invalid input");
else {
static char line1[MAX_VALUE_FOR_ARRAYS];
static char line2[MAX_VALUE_FOR_ARRAYS];
/* to have \n at the end like the read lines, it is also possible
to read the name with fgets but in case of extra spaces at the end it is not found */
strcat(contact_name, "\n");
while ((fgets(line1, MAX_VALUE_FOR_ARRAYS, fp) != NULL) &&
(fgets(line2, MAX_VALUE_FOR_ARRAYS, fp) != NULL))
{
if ((strcmp(line1 + /*bypass "First Name : "*/ 13, contact_name) == 0) || /* first name */
(strcmp(line2 + /*bypass "Last Name : "*/ 12, contact_name) == 0)) { /* last name */
/* that one */
fputs(line1, stdout);
fputs(line2, stdout);
if (fgets(line1, MAX_VALUE_FOR_ARRAYS, fp) != NULL) {
fputs(line1, stdout);
if (fgets(line1, MAX_VALUE_FOR_ARRAYS, fp) != NULL) {
fputs(line1, stdout);
if (fgets(line1, MAX_VALUE_FOR_ARRAYS, fp) != NULL) {
fputs(line1, stdout);
if (fgets(line1, MAX_VALUE_FOR_ARRAYS, fp) != NULL) {
fputs(line1, stdout);
}
}
}
}
break;
}
else {
fgets(line1, MAX_VALUE_FOR_ARRAYS, fp);
fgets(line1, MAX_VALUE_FOR_ARRAYS, fp);
fgets(line1, MAX_VALUE_FOR_ARRAYS, fp);
fgets(line1, MAX_VALUE_FOR_ARRAYS, fp);
}
}
fclose(fp);
}
}
return 0;
}
Examples :
pi#raspberrypi:/tmp $ ./a.out
Enter the name of the contact you want to search : Tony
First Name : Tony
Last Name : Stark
Address : Malibu
Phone number : 10203044032
E-mail : tony.stark#jarvis.com
Company / Place of work : Stark Industries
pi#raspberrypi:/tmp $ ./a.out
Enter the name of the contact you want to search : Stark
First Name : Tony
Last Name : Stark
Address : Malibu
Phone number : 10203044032
E-mail : tony.stark#jarvis.com
Company / Place of work : Stark Industries
pi#raspberrypi:/tmp $ ./a.out
Enter the name of the contact you want to search : Bruno
First Name : Bruno
Last Name : Pages
Address : somewhere in France
Phone number : 123456789
E-mail : me#hiddenAddress.fr
Company / Place of work : BoUML unlimited
Supposing addressBook.txt contains :
First Name : Tony
Last Name : Stark
Address : Malibu
Phone number : 10203044032
E-mail : tony.stark#jarvis.com
Company / Place of work : Stark Industries
First Name : Bruno
Last Name : Pages
Address : somewhere in France
Phone number : 123456789
E-mail : me#hiddenAddress.fr
Company / Place of work : BoUML unlimited
I am currently learning reading from files in C.
Anyway, cutting to the chase:
Text file content:
123456 James Doakes; 0
987987 Dexter Morgan; 0
010203 Masuka Perv; 0
int main()
{
char accountNr[ACCOUNTNRSIZE], ownerName[NAMESIZE], enter[3];
int accountBalance = 0;
char filename[] = "breg.txt";
FILE *file = fopen(filename, "r");
if (file != NULL) {
char line[128];
while (fgets(line, sizeof(line), file) != NULL) {
sscanf(line, "%s %[^;] %d ", accountNr, ownerName, &accountBalance);
printf("%s", ownerName);
//fflushstdin();
}
fclose(file);
} else {
perror(filename);
}
return 0;
}
I wrote this to check if the name for instance James Doakes was registered correctly :
printf("%s", ownerName);
But when it prints that out it's like the stdout is still active and I can push Enter and it will type the name again. My goal is to of course be able to sscanff the number, the full name, and the last number as seperate variables. But it obviously doesn't work. I am guessing a \n gets registered as well. Dunno, I am just speculating.
What am I doing wrong? Why? And how do I solve this?
Much appreciated,
Mif
%s %[^;] %d
means a string terminated by white space, optional white space, a sequence of characters that are not ;, optional white space, then a number.
You appear to be not scanning for the actual ; character itself so that, when you try to get the number, the ; in the input stream will cause it to fail. You can see this with:
#include <stdio.h>
#define ACCOUNTNRSIZE 100
#define NAMESIZE 100
int main (void) {
char accountNr[ACCOUNTNRSIZE], ownerName[NAMESIZE], enter[3];
int accountBalance = 0;
char filename[] = "breg.txt";
FILE *file = fopen(filename, "r");
if (file != NULL) {
char line[128];
while (fgets(line, sizeof(line), file) != NULL) {
int count = sscanf(line, "%s %[^;] %d ", accountNr, ownerName, &accountBalance);
printf ("%d [%s] [%s] [%d]\n", count, accountNr, ownerName, accountBalance);
}
fclose(file);
} else {
perror(filename);
}
return 0;
}
which outputs:
2 [123456] [James Doakes] [0]
2 [987987] [Dexter Morgan] [0]
2 [010203] [Masuka Perv] [0]
In fact, even if you change the breg.txt file to be:
123456 James Doakes; 314159
987987 Dexter Morgan; 271828
010203 Masuka Perv; 42
you still get 0 for the account balance because the scanning only successfully reads two items.
Whenever you use one of the scanf-family functions, you should check the return code to ensure it's scanning the correct number of items, as in:
int count = sscanf (line, "%s %[^;] %d ", accountNr, ownerName, &accountBalance);
if (count != 3) {
fprintf (stderr, "Catostrophic failure, count is %d\n", count);
return 1;
}
The fix here is relatively simple, just use %s %[^;]; %d as the format string.
With that change, the output you see is:
3 [123456] [James Doakes] [314159]
3 [987987] [Dexter Morgan] [271828]
3 [010203] [Masuka Perv] [42]
Keep in mind you don't actually need a space before the %d (though it causes no harm). That particular format specifier skips white space before attempting to scan the number.