After reading from text file, results are displayed incorrectly - c

I've written a program that reads four variables (three strings and one character) every line from a text file. But when I display the variables, an unexpected character pops up at the end of each line. (I've ensured that the lengths of the variables are large enough).
Why is this? (Overflowing buffers, again?) And how do I fix this?
Text file contents:
M0001 Cool Name F 123-456789
M0002 Name Cool M 987-654321
Code:
#include <stdio.h>
#include <stdlib.h>
int main() {
FILE *text;
char id[6], name[101], gender, contact[13];
text = fopen("test.txt", "r");
while (fscanf(text, "%s %[^\n]s %c %s\n", id, name, &gender, contact) != EOF)
printf("%s %s %c %s\n", id, name, gender, contact);
fclose(text);
return 0;
}
The output I expect:
M0001 Cool Name F 123-456789
M0002 Name Cool M 987-654321
What I get instead:
M0001 Cool Name F 123-456789 1⁄4
M0002 Name Cool M 987-654321 1⁄4

in the call to fscanf(), the format string: "%s %[^\n]s %c %s\n" is not correct.
the '[^\n]' will read to the end of the line (which will overflow the input buffer: `name'. Then the next char is NOT an 's' because the next character is the newline.
should compare the returned value to 4, not EOF
the input/format specifiers '%[...]' and '%s' have no problem overflowing the input buffer, so should ALWAYS have a MAX_CHARACTERS modifier that is one less than the length of the input buffer (those format specifiers always append a NUL byte to the input
The following proposed code:
cleanly compiles
documents why each header file is included
performs the desired functionality
splits the 'name' into 'firstname' and 'lastname' for easier handling and to match the format of the input data
properly checks the returned value from fscanf()
properly checks for any error from fopen() and if an error is returned, properly outputs the error message and the text indicating why the system thinks the function failed to stderr
uses an appropriate format string for the calls to fscanf() and printf()
replaces 'magic' numbers with meaningful names via a enum statement
And now the proposed code:
#include <stdio.h> // fopen(), fclose(), fscanf(), perror(), printf()
#include <stdlib.h> // exit(), EXIT_FAILURE
enum{
MAX_ID_LEN = 6,
MAX_NAME_LEN = 20,
MAX_CONTACT_LEN = 13
};
int main( void )
{
char id[ MAX_ID_LEN ];
char firstname[ MAX_NAME_LEN ];
char lastname[ MAX_NAME_LEN ];
char gender;
char contact[ MAX_CONTACT_LEN ];
FILE *text = fopen("test.txt", "r");
if( !text )
{
perror( "fopen to read 'test.txt' failed" );
exit( EXIT_FAILURE );
}
// implied else, fopen successful
while (5 == fscanf(text, "%5s %19s %19s %c %12s",
id, firstname, lastname, &gender, contact) )
{
printf("%s %s %s %c %s\n",
id, firstname, lastname, gender, contact);
}
fclose(text);
return 0;
}

%[^\n]s eats up everything from that point on and puts it in name. So only id and name are filled. gender and contact have 'random' contents coming from the program stack (as they are not initialized).
By accident the your stack had 1/4 in gender + contact.
On my machine, the program crashes.

As the number of space-delimited words in your name apparently is variable, you can only use %[^\n]s to grab "as much as possible" – but that will also eat up any and all following relevant data. A quick solution would be to re-design the input format and place the name at the very end; then, your fscanf argument would be:
"%s %c %s %s\n", id, &gender, contact, name
Alternatively, rewrite the code to use less fscanf and more 'manual' parsing:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
int main (void)
{
FILE *text;
char id[6], name[101], gender, contact[13];
char *lookback;
int result;
unsigned int line_number = 0;
text = fopen ("test.txt", "r");
if (text == NULL)
{
printf ("file not found!\n");
return EXIT_FAILURE;
}
do
{
result = fscanf(text, "%s %[^\n]s\n", id, name);
line_number++;
if (result == EOF)
break;
if (result != 2)
{
printf ("error in data file on line %u (expected at least 2 items)\n", line_number);
break;
}
/* at this point, 'name' also contains 'gender' and 'contact' */
lookback = strrchr (name, ' ');
if (lookback == NULL || strlen(lookback+1) > 12)
{
printf ("error in data file on line %u (expected 'contact')\n", line_number);
break;
}
/* lookback+1 because lookback itself points to the space */
strcpy (contact, lookback+1);
/* cut off at lookback */
*lookback = 0;
lookback = strrchr (name, ' ');
if (lookback == NULL || strlen(lookback+1) != 1)
{
printf ("error in data file on line %u (expected 'gender')\n", line_number);
break;
}
/* lookback now points to the space before the gender */
gender = toupper(lookback[1]);
if (gender != 'F' && gender != 'M')
{
printf ("error in data file on line %u (expected 'M' or 'F')\n", line_number);
break;
}
/* cut off again at lookback; now name is complete */
*lookback = 0;
printf ("%s %s %c %s\n", id, name, gender, contact);
} while (1);
fclose(text);
return EXIT_SUCCESS;
}
This method does have a couple of associated drawbacks. One of the perks of scanf is that it normalizes whitespace; multiple spaces and tabs (or even returns) will silently be translated to a single space before scanning. This code, on the other hand, explicitly checks for a single space character. If there is variation in the whitespace in your data file, you must account for that as well.
With the last two items 'manually' processed, you can opt to not use fscanf at all. You can read an entire line of text at once with fgets (which also has a line length check built in) and look for spaces using strchr and strrchr. To counter possible whitespace problems, search the line for tabs and double spaces and change these to a single space.

Related

Read struct array in files and printf specific line on certain condition [c language]

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

How to read from an input file and save certain parts of each line and output it to the command line?

This is the C code I have so far. I am reading the first name and last name from the input file but the thing that is giving me trouble is to print out the other stuff.
I have to take a line like this:
Venus Jensen 33770530841 vbjensen#oqtu.edu FRNO 624-771-4676 SIJ SBE WHV TVW
and remove the extra stuff to make it like this:
vbjensen Venus Jensen (624)771-4676
My problem is that I am getting the right output but for some of the lines that(1) don't have the FRNO or something equivalent and (2) not having the # symbol, the line still shows up. For example, the lines:
Noe Richard 974927158 nirichar#bvu.edu 079-651-3667 HAVQ
Phillip Sandoval 836145561 pusandov#luu.edu OXRU 697-728-1807 LHPN GUX
These lines should not be printed since the first one does not have the FRNO equivalent and the second one does not have the # symbol. Every time I try to add the format operation to match but not save, the program sscanf function starts to mess up.
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
int main()
{
// Open the input file and print an error message if we're unsuccessful.
// (the error message is mostly to help you with debugging. We won't test
// this behavior).
FILE *fp = fopen("input.txt", "r");
char line[500];
if(!fp) {
printf("Can't open input file\n");
exit(1);
}
// Counting input lines, so we can report errors.
// Keep reading input lines until we reach the end-of-file.
// Write an output line or an error message for each one.
do {
int lineCount = 1;
char fName[12];
char lName[12];
//char skipNum[12];
char email[9];
//char firstNum[4];
//char secondNum[4];
//char thirdNum[5];
//printf("%c", ch);
char phone[] = "(123)123-1234";
//fscanf(fp, "%s", fName);
//fscanf(fp, "%s", lName);
//fscanf(fp, "%[1-9]", skipNum);
//fscanf(fp, "%[a-z]", email);
sscanf (line, "%11s%11s%*[ 0-9]%9[^#]%*[^0-9]%3c-%3c-%4c", lName, fName, email, &phone[1], &phone[5], &phone[9]);
//printf("Invalid line");
//printf("\n");
// exit(1);
printf("%s", line);
printf("\n");
printf("%s", email);
printf("%s", fName);
printf("%s", lName);
//printf("%s", skipNum);
//printf("%s", firstNum);
printf("%s", phone);
printf("\n");
lineCount++;
}
while (fgets(line, sizeof line, fp));
return EXIT_SUCCESS;
}
In the format string "%20s%20s%*[ 0-9]%20[^#]#%*s%20s %3c-%3c-%4c"
%20s will scan up to 20 non-whitespace characters. Ignoring leading whitespace and stopping at trailing whitespace.
%*[ 0-9] will scan spaces and digits. The asterisk, *, tells sscanf to discard the scanned characters.
%20[^#]# will scan up to 20 characters or will stop scanning at a #. Then it will try to scan a #. If the # is missing the scan will terminate early.
%*s will scan non-whitespace and discard the characters.
%20s will scan up to 20 non-whitespace characters.
%3c will ignore any leading whitespace and scan three characters.
-%3c will scan a - and then three characters. If the - is missing the scan will terminate early.
-%4c will scan a - and then four characters. If the - is missing the scan will terminate early.
If sscanf does not scan seven items, nothing will be printed.
#include <stdio.h>
#include <stdlib.h>
int main ( void) {
char line[500] = "";
int lineCount = 0;
FILE *fp = NULL;
if ( NULL == ( fp = fopen("input.txt", "r"))) {
fprintf( stderr, "Can't open input file\n");
exit(1);
}
while ( fgets ( line, sizeof line, fp)) {//read each line from the file
char fName[21];
char lName[21];
char match[21];
char email[21];
char phone[] = "(123)567-9012";
lineCount++;
if ( 7 == sscanf ( line, "%20s%20s%*[ 0-9]%20[^#]#%*s%20s %3c-%3c-%4c"
, lName, fName, email, match, &phone[1], &phone[5], &phone[9])) {
printf ( "line [%d] %s %s %s %s\n", lineCount, email, fName, lName, phone);
}
}
fclose ( fp);
return 0;
}

C How to use sscanf properly support

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.

fscanf copies a line from file twice in C

while(1<2) {
nscan=fscanf(infile, "%s %s %d%c",temp.name, temp.surname,&temp.code,&termch);
if(nscan==EOF) break;
if(nscan!=4 || termch!='\n')
printf("Error\n");
RecBSTInsert(&a,temp);
}
for some reason nscan //if(nscan==EOF) break; does not get executed when it is supposed to and it runs one more time giving the binary tree one more value, which is same with the last one.
fscanf:
Upon successful completion, these functions shall return the number of
successfully matched and assigned input items.
fscanf does not return the input provided. That is why you are not seeing EOF like you are testing for.
See: manpage for *scanf functions.
To answer your other question, try looping through like this:
while (fscanf(infile, "%s %s %d%c, temp.name, temp.surname, &temp.code, &termch) == 4)
{
//...
}
Edit again: I threw together a small program to simulate what I think you are doing. Here is my implementation, which takes from a file "testfile.txt" which looks like:
Bill Person 1
Bob Dog 2
Andrew Cat 3
With only one newline between each line. It matches the pattern %s %s %d%c (where \n is the %c).
Program to use this file:
#include <stdio.h>
#include <stdlib.h>
struct test {
char name[64];
char surname[64];
int code;
};
int main()
{
struct test temp;
char endchar;
FILE *infile = fopen("./testfile.txt", "r+"); // works with "r" as well, just a habit
if (infile == NULL)
return -1;
while (fscanf(infile, "%s %s %d%c", temp.name, temp.surname,
&temp.code, &endchar) == 4)
{
printf("Got one!\n");
}
return 0;
}
Of course, the printf exists in place of whatever logic you want to do on the current "temp" input.

Can't get txt file to print correctly

This is a homework assignment for my C Programming class.
I am given a text file, with two data columns; the first column is the age; the second column is the avgprice. I'm able to read and print the values fine. However, for some reason the age and avgprice are flipped in the output. I have no clue why.
Here is the code
#include "stdafx.h"
#include <stdio.h>
int main() {
double age, avgprice; //age = 1st column, avgprice = 2nd column
FILE *corolla; //ptr for file
char eof; //needed for end of file check
corolla = fopen("C:/Users/Nate/Downloads/DataFiles/corolla.txt", "r");
if (corolla == NULL) { //makes sure the file exists
printf("File does not exist!\n");
return 0; //prevents crashing
}
else {
printf("Age \t\t Average Price\n"); //header for data when printed
/*prints values until we're at the end of the file*/
while (fscanf(corolla, "%c", &eof) != EOF) {
fscanf(corolla, "%lf %lf", &age, &avgprice); //scans in data from file
printf("%.1f \t\t $%.2f\n", age, avgprice); //prints data from file
}
}
fclose(corolla); //closes file
return 0;
}
This is what the output looks like
It's puzzling to me because I have used this exact format to do the same thing with other data files--no issues. For some reason, this one file is having difficulty.
Here is the datafile I'm supposed to be reading. I've uploaded it to my Dropbox that way you can inspect the formatting if necessary. Corolla.txt
This line:
while (fscanf(corolla, "%c", &eof) != EOF)
reads a character from the file. The first character in the file is 1 so it reads that 1 into eof.
Your next line is:
fscanf(corolla, "%lf %lf", &age, &avgprice);
which reads the next two entries from the file, which are 13990 and 2, in that order. So the first age is 13990 and the first avgprice is 2.
After that, the file pointer is now pointing to the blank space after the 2. When you go:
fscanf(corolla, "%c", &eof)
it reads a space into eof.
Then when you get to:
fscanf(corolla, "%lf %lf", &age, &avgprice);
It reads the next two values, 13495 and 3 respectively. And so on.
To fix this you should stop doing fscanf(corolla, "%c", &eof). I don't know what you are expecting this to do exactly , but it does not test whether you're at the end of the file or not. Instead it reads a character, ignores the character, and checks the return value of fscanf.
To fix your code:
while (2 == fscanf(corolla, "%lf %lf", &age, &avgprice))
{
printf("%.1f \t\t $%.2f\n", age, avgprice); //prints data from file
}
The return value of fscanf is the number of items successfully read (if it succeeded). When it returns something other than 2 you know you must have hit the end of the file.
Your input file uses a line-based format. fscanf reads the input chunk by chunk. A chunk is usually something separated by white space, which can be space, tabs or even the new-line. Therefore fscanf is not suited to read line-based formats.
In my opinion, it is better to read the input in two steps: first, read a line with fgets, then read the data from that line with sscanf. For example:
#include <stdlib.h>
#include <stdio.h>
int main()
{
FILE *f;
int line = 0;
f = fopen("kk", "r");
if (f == NULL) {
printf("File does not exist!\n");
return 0;
}
printf("%20s%20s\n", "age", "avg. price ($)");
for (;;) {
char buffer[80];
int age, price;
if (fgets(buffer, sizeof(buffer), f) == NULL) break;
line++;
if (sscanf(buffer, "%d %d", &age, &price) < 2) {
printf("(Skipping bad input in line %d).\n", line);
} else {
printf("%20d%20d\n", age, price);
}
}
fclose(f);
return 0;
}
This also gives you a kind of low-level error reporting.
Also, there's usually no need to do extra checking for EOF. The file input functions return a special value when the end of the file is reached. fscanf and getc return EOF; fgets returns NULL. It is usually always better to stop reading based on these return values.
In your case, the fscanf("%c", &oef) eats up the first character in your file, the digit 1. Luckily, after that it only feeds on new-line so your input doesn't get tripped up worse. (But change your scan format to "%lf %lf " for a drastic price reduction.)

Resources