I am trying to read in from a file, and I can't get the pattern of it right. Can someone tell me what I can do to get it working?
int main()
{
char name[20];
int age;
float highbp, lowbp, risk;
FILE *fp;
fp = fopen("data.dat", "r");
if(fp == NULL){
printf("cannot open file\n\n");
}
while(fscanf(fp, "name:%s\nage:%d\nbp:%f\nrisk:%f", name, &age, &highbp, &risk) != EOF){
}
printf("Name: %s\n", name);
printf("%d\n", age);
printf("%f\n", highbp);
printf("%f\n", risk);
}
data.dat:
name:tom
age:32
bp:43.00
risk:0.0
If it can't open the file it prints a message, but then continues. Instead it should return from main.
if (fp == NULL) {
printf("cannot open file\n\n");
return 1;
}
fscanf will return the number of items parsed, so it's probably safer to stop reading when the number returned < 4 (not all the items could be read).
Presumably "data.dat" contains multiple records and each line has a line ending. This means that after reading the first record the next character in the file is the line ending for the "risk:0.0" line. You should end the fscanf template with \n.
This is because the second time it tries to parse the file, fscanf will see that character, which it isn't expecting (the fscanf template starts "name:"), so it will stop reading, and you'll get only the first record.
You should change the "name" format specifier from %s to %19s to make it read at most 19 characters (+terminating '\0'). The way you have it now is a guaranteed failure in case someone gives you 20+ character name.
Can someone tell me what I can do to get it working?
I suggest you separate the functionality in different statements.
Don't try to cram all of the program functionality in 1 statement.
Your big statement is doing 3 things:
it is reading data from file
it is comparing the return value of scanf with EOF
it is controlling when to stop reading
I suggest you do (at least) 3 different statements for the 3 different actions.
Hint: comparing the return value of scanf only with EOF is a little too short
Related
i am new in c. So in my university, i just learn about file in c. and i got a task. If i put an empty file in my project directory, and read it. The output are symbols (i dont know what symbol it is). So here is the code, please help
player dota[100];
FILE *fp;
fp = fopen("soal09.txt", "r");
if(fp == NULL)
{
printf("Error Opening The File!!\n");
return 0;
}
else
{
while(!feof(fp))
{
fscanf(fp, "%[^ ] %d %d\n", &dota[idx].name, &dota[idx].score, &dota[idx].num);
idx++;
}
}
fclose(fp);
do
{
enter();
menu();
printf("Input your choice [1..5]: ");
scanf("%d", &choose); fflush(stdin);
if(choose == 1)
{
system("cls");
enter();
printf("%-20s %-15s %s\n", "Player Name", ": Average Score", ": Number of Playing");
printf("====================================================================\n");
for(int i = 0; i < idx; i++)
{
printf("%-20s %-15d %d\n", dota[i].name, dota[i].score, dota[i].num);
}
printf("\nPress Enter to Continue...");
getchar();
}
getchar();
return 0;
}
and the output is ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠ -858993460
Thank you ^^
The end-of-file indicator that is checked by feof() is only set after a previous file I/O operation has failed. You must attempt an I/O operation to find out if you have reached the end of the file. So, with an empty file, your code attempts to read the file, the end-of-file indicator is set, no values are read into the first struct, but idx is incremented, so it looks like you have added data for a player. But the fields of the first struct are uninitialized, so you are seeing garbage. Also note that dota[idx].name is presumably an array of chars, so it decays to a pointer to char when passed to fscanf(). Using &dota[idx].name, as you have, is wrong, though it might appear to work. It does cause the compiler to emit a warning, and you should have these enabled (I always use at least gcc -Wall -Wextra -pedantic).
You should not use feof() to control file I/O loops. One simple solution is to use the return value of fscanf() to control the loop:
while(fscanf(fp, "%[^ ] %d %d\n",
dota[idx].name, &dota[idx].score, &dota[idx].num) == 3) {
idx++;
}
This will only update a player struct if three assignments are made by the call to fscanf(). But, the problem with this simple solution is that it doesn't handle malformed input well. If a line of the data file is missing a field, the struct will be incorrectly filled, and the loop will terminate, even if there are more lines in the file to read. Also, since no field width is specified for the string conversion, a long name could crash your program.
A better solution uses fgets() to read the lines of the file into a buffer, and then uses sscanf() to extract the information from the buffer:
#include <string.h> // for strcpy()
...
char buffer[1000];
int line = 0;
char temp_name[100];
int temp_score, temp_num;
while(fgets(buffer, sizeof(buffer), fp)) {
++line;
if (sscanf(buffer, "%99[^ ] %d %d\n",
temp_name, &temp_score, &temp_num) == 3) {
strcpy(dota[idx].name, temp_name);
dota[idx].score = temp_score;
dota[idx].num = temp_num;
++idx;
} else if (buffer[0] != '\n') {
fprintf(stderr, "Bad input in line %d\n", line);
}
}
Here, a generous buffer is declared to accept a line of text from the file, and temporary variables are declared to hold the values to be stored in the struct fields. I have chosen a size of 100 for temp_name, but this should match the size of the string array in your actual struct. Note that the string conversion in sscanf() has a field width of 99, so that at most 99 non-space (not non-whitespace, and why aren't you just using %99s here?) characters are matched, leaving space for the '\0' to be added.
fgets() will return NULL when it reaches the end of the file, so the loop will continue until that happens. For each line that is read, a line counter is incremented. Then sscanf() is used to read data into the temporary variables. The value returned from sscanf() is checked to be sure that 3 assignments were made, and if so, then the data is copied into the struct, and idx is incremented. Note that strcpy() is needed to copy the string from temp_name to dota[idx].name.
If the value returned from sscanf() indicates that something other than 3 assignments were made, there is a check to see if buffer holds an empty line. If not, an error message is printed to stderr providing the line number of the bad input in the file.
A couple of further comments. Your do loop appears to be missing the associated while(). And you use fflush(stdin) after the scanf() inside the do loop. fflush()is meant to flush output streams. The behavior of fflush() on input streams is explicitly undefined in the C Standard (ISO/IEC 9899:2011 7.21.5.2/2), though I believe that Microsoft deviates from the Standard here. Nevertheless, you should not use fflush(stdin) in portable C code. Instead, use something like this:
int c;
...
scanf("%d", &choose);
while ((c = getchar()) != '\n' && c != EOF)
continue; // discard extra characters
This code reads characters from the input stream until either a '\n' or EOF is reached, clearing any characters left behind by the previous call to scanf() from the input stream.
I execute my program from shell like that :
$main.exe < input.txt
in input.txt I have digits (Count of these digits is unknown)
in my program I doing something like :
while(1)
{
int xCoordinate, yCoordinate;
scanf("%d %d", &xCoordinate, &yCoordinate);
......
}
How can I break this loop when there is no value to read?
Assuming that the input is consistent, you can do it like this:
if (scanf("%d %d", &xCoordinate, &yCoordinate) != 2) break;
The reason this would work is that scanf family of functions return the number of entries that they assigned. In your code, you want scanf to assign two items; if EOF is reached instead, a value smaller than 2 would be returned.
Note: that this approach will break at the first spot where the input file is not consistent with the format that you expect. For example, if your input has a string in place of one of the numbers, the loop would exit after making a failed attempt to interpret that string as a number.
You have to separate the "reading from a file (or stdin)" from the "parsing the line I read. You will get terribly wrong answers if the data is not perfectly what you expect.
You get fine control with something like
char buffer[BUFSIZ];
int xCoordinate, yCoordinate;
while(fgets(buffer, BUFSIZ, stdin) != NULL) {
if(sscanf(buffer, "%d %d", &xCoordinate, &yCoordinate) != 2) {
fprintf(stderr, "parsing error\n")
exit(1);
}
}
Even this leaves a bit to be desired because if fgets returns NULL it can either mean EOF or "read error" but it is far more robust than scanf and keeps to the spirit of the original.
I normally don't ask questions on here unless I'm really stuck! I was wondering if anyone can please explain why my code prints out a '5 47'. I understand why there is a 5, but not why there is a 47? I looked up the ASCII values for blankspace (32) and I tried changing the second letter to e, f, g, for example but the output remains '5 47' unchanged.
In general, when I use fscanf(fp, "%d", &variablename), does the fscanf skip over miscellaneous characters? For example: in my file test.txt I had "5 hello 6 ben jerry\n". How would I scan in the 5 and the 6? Would fscanf(fp, "%d %d", &test1, &test2) scan in the 5 and 6, skipping over the word "hello"?
Here is my simple code I am using to test output:
int main(int argc, char *argv[]) {
int blah, test;
FILE * fp;
fp = fopen(argv[1], "r");
fscanf(fp, "%d %d", &blah, &test);
printf("%d %d\n", blah, test);
return 0;
}
My file I am using as argv[1] contents:
5g
P.S. is FILE *fp an actual pointer to each character/number and does it work as a placeholder when it scans through the file? Is that why we need rewind(fp) once it hits the end of the file?
The operator %d looks for an integer, not a character. Because g is a character, not an integer, %d is getting confused and the output will not always be 5 47. The 47 could be anything. it could be 5 7, 5 23 etc. This is because the fscanf is not reading a second number, so no value is being assigned to test. Therefore, test remains at the value which was sitting in that piece of memory when the program was initiated.
To fix this, replace %d with %c and change the type of blah and test to int. Also, as WhozCraig said, it is good practise to check the return value of fscanf to check that two values have been found. This way, you can be sure that everything you're looking for has been found.
Note that the scanf() family of functions stop reading when they come across a character that is not expected by the format string. The unexpected character is left in the input for the next input operation to process.
If you want to read two integers that are definitely separated by a 'word' that is not an integer, then you will need to skip the word. If you don't know in advance what the word will be, you need to use assignment suppression (see the POSIX scanf() page for lots of information).
Hence, your code to read the two integers from input containing
5 hello 6 ben jerry
should be:
if (fscanf(fp, "%d %*s %d", &blah, &test) != 2)
…Oops; format error?…
Note that the code tests that it got the expected result. However, if you don't know whether there'll be a word between the two numbers, you are much better off using fgets() and sscanf() because you can try different parses of the same line:
char buffer[4096];
while (fgets(buffer, sizeof(buffer), fp) != 0)
{
if (sscanf(buffer, "%d %*s %d", &blah, &test) == 2)
…got two numbers with a word — let's go!
else if (sscanf(buffer, "%d %d", &blah, &test) == 2)
…got two numbers but no word — let's go!
else
…didn't recognize the format…
}
One of the major advantages of this is that you can report the error in terms of the complete line of input, rather than just the part that fscanf() didn't manage to work on.
Your last question, about a FILE *, is not a pointer to each character in the file. It is a handle which allows you to invoke functions that take a file pointer argument to read from or write to the associated file. However, you can't use indexing based on the file pointer (so fp[1024] does not identify the character at offset 1024 in the file or anything useful like that). If you want that sort of behaviour, you need a memory-mapped file (mmap() for POSIX systems).
I understand that fgets reads until EOF or a newline.
I wrote a sample code to read lines from a file and I observed that the fgets gets executed more than the desired number of times. It is a very simple file with exactly two blank lines, ie. I pressed enter once and saved the file.
Below is the code:
fp = fopen("sample.txt","r");
while (!feof(fp)) {
fgets(line,150,fp);
i++;
printf("%s",line);
}
printf("%d",i);
Why is the while loop getting executed three times instead of 2, as there are only two blank lines in the file?
In your case, the last line is seemingly read twice, except that it isn't. The last call to fgets returns NULL to indicate the the end of file has been read. You don't check that, and print the old contents of the buffer again, because the buffer wasn't updated.
It is usually better not to use feof at all and check return values from the f... family of functions:
fp = fopen("sample.txt", "r");
while (1) {
if (fgets(line,150, fp) == NULL) break;
i++;
printf("%3d: %s", i, line);
}
printf("%d\n",i);
The function feof returns true after trying to read beyond the end of the file, which only happens after your last (unsuccessful) call to fgets, which tries to read at or rather just before the end of the file. The answers in this long SO post explain more.
Having trouble understanding a program(below).
I am little bit confused about the statement fputs("\n",fp)
eg. let me type:
It doesn't matter what you are underneath
Its what you do that defines you.
If I don't mention fputs("\n",fp) the string appears in a single line. But with the code it is saved as typed.
Now the question is how the \n is inserted in the desired place, cause' normally the \n should be appended in the end of the text.
Any help would be seriously appreciated.
int main()
{
FILE *fp;
char s[80];
fp=fopen("abc.txt","w");
if(fp==NULL)
{
puts("Cannot open file");
exit(1);
}
printf("\nEnter a few lines of text:\n");
while(strlen(gets(s))>0)
{
fputs(s,fp);
fputs("\n",fp);
}
fclose(fp);
return 0;
}
gets (which shall not be used and has actually been removed from the most recent C standards) does not save the \n in its buffer (while fgets does).
And fputs, unlike puts, does not automatically insert one at the end of the string it writes. So by adding a fputs("\n", fp); (or fputc('\n', fp)) after outputting each typed line, you insert the missing newline in the file.
fputs does not automatically add a newline to the output (in contrast with puts which does).