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).
Related
I have a problem. I have two file tests. Let's suppose that 'file' both of the the file names.
I'm going to test on both of the files and one of them is like this:
1 2
and the other one is :
1 2
2
4
3
6
What I want to do is read the the file and save the numbers and test them. So if there's 2 numbers, it saves the 2 numbers and does a TEST in the numbers and closes the file but if there's 4 or 6 or 8 numbers in the file, I want to repeat my function BODY and only close my file until there's no numbers left. But there's one problem, I'm using the condition IF(fscanf(fp, "%d", nova)) to check if there's anything left and if there are still numbers, I would have scanned a number that should be in the variable HEADS.
What I want to do is check if there's more numbers besides the first two and if there is, I want to start my function again and not skip a number because of the fscanf condition.
int Body(){
int Head, Arms;
int nova;
FILE *fp;
fp=fopen("file", "r");
fscanf(fp, "%d", &Head);
fscanf(fp, "%d", &Arms);
test(head, arms);
if(fscanf(fp, "%d", &nova)==EOF) fclose(fp);
else ....
return 0;
}
Use a loop instead of an extra scanf() to check if you've reached the end.
while (fscanf(fp, "%d %d", &Head, &Arms) == 2) {
...
}
If you're using a seekable file, you can store the current position using ftell and then try to read the next number - if scanf was successful, fseek back to the position returned by ftell and call the function.
If you're just reading from a generic stream that might not be seekable, you can peek one character forward. I.e. you can use a space at the end of the *scanf format to eat the white space, and then try to read one more character using getchar/fgetc- if this looks like a digit, you can useungetc` to push it back to the stream.
I've got a little problem:
I want fgets() to act like scanf("%d",...) - read input to whitespace, not whole line. Is there any way to make it work like that?
Thanks in advance
Use fgets() to save the whole line to a char array. Then write a function that uses strtok() to slice your line into substrings, separated by spaces, and check each substring to see if it contains only digits. If it is so, use sscanf() to read from that substring to a variable.
Alternatively, you can use fscanf() in the first place, with format "%s" to read a string from the file. fscanf() will stop reading upon reaching a separator (space, new line, etc). Check the string read and if it contains a valid number, use sscanf() or atoi() to convert it into a numeric value.
I've come up with this code:
#include <stdio.h>
#define VALUE_NOT_PRESENT -1 /* A value you won't expect in your file */
int main()
{
FILE *f;
char s[256];
int n;
f = fopen ("test.txt","r");
fscanf (f, "%s", s);
while (!feof(f))
{
n = VALUE_NOT_PRESENT;
sscanf (s, "%d", &n); /* if s cannot be converted to a number, n won't
be updated, so we can use that to check if
the number in s is actually a valid number */
if (n == VALUE_NOT_PRESENT)
printf ("Discarded ");
else
printf ("%d ", n);
fscanf (f, "%s", s);
}
fclose (f);
printf ("\n");
return 0;
}
It works by using the ability of *scanf family functions to not update the variable if the characters read cannot form a valid number.
Executed with a file with this content:
1 2 -3
-4 abc
5 6 a12 6c7
It's able to recognize abc and a12 as invalid numbers and so they are discarded. Unfortunately, it recognized 6c7 as the number 6. I don't know if this is ok for you. If not, you will probably have to write a function that will use a state-machine driven parser to accept or reject the string as a number. I don't know if such function exists in the standard library, but will be surely available out there.
my question is simple, but I can't find it on google, so here I am.
Basically, as an example, I am reading in a bunch of integers until EOF from an input file. I used fgetc to check, but then it moves the file pointer to the address of the 2nd integer. How can I check for EOF in this while loop without moving the file pointer when it checks?
Keep in mind I will be doing something much more complex with this loop than scanning in integers. Also, don't mention my use of fscanf rather than fgets, I know. Its just a simple example to show you what I mean.
while(fgetc(ifp) != EOF)
{
fscanf(ifp, "%d", &test);
printf("%d\n", test);
}
If the input file has integers 1-10 for example, the above code would print:
2
3
4
5
6
7
8
9
10
Missing the 1!
fgetc Returns the character currently pointed by the internal file position indicator of the specified stream. The internal file position indicator is then advanced to the next character.Using do{ }while(); will solve your problem.
You should consider EOF conditions to be an implementation detail. What's really important here is whether fscanf has successfully returned a value.
while(fscanf(ifp, "%d", &test) == 1) {
printf("%d\n", test);
}
On the other hand, this feels like one of those times where it makes sense to put the logic in the middle of the loop.
while(1) {
int ret = fscanf(ifp, "%d", &test);
if (ret != 1)
break;
printf("%d\n", test);
}
Especially for debugging, it can be nice to split things up into separate statements with variables that can be inspected.
For various reasons, it's impossible to determine if EOF has been reached without actually performing a prior read from the file.
However, using a seperate function call to do this is a bad idea, even if you were to use ungetc() to put back the character you tried to read.
Instead, check the return value of the call to fscanf() itself:
while(fscanf(ifp, "%d", &test) == 1)
{
printf("%d\n", test);
}
How about simply:
while (fscanf (ifp, "%d", &test) != EOF)
printf("%d\n", test);
fgetc(ifp) advance the file pointer, hence skipping your first integer
Instead Use: -
while(1)
{
if(fscanf(ifp, "%d", &test)!=1)
break;
printf("%d\n", test);
}
The real answer is to use feof
https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.3.0/com.ibm.zos.v2r3.bpxbd00/feof.htm
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
I am reading set of numbers from file by fscanf(), for each number I want to put it into array. Problem is that thoose numbers are separated by "," how to determine that fscanf should read several ciphers and when it find "," in file, it would save it as a whole number? Thanks
This could be a start:
#include <stdio.h>
int main() {
int i = 0;
FILE *fin = fopen("test.txt", "r");
while (fscanf(fin, "%i,", &i) > 0)
printf("%i\n", i);
fclose(fin);
return 0;
}
With this input file:
1,2,3,4,5,6,
7,8,9,10,11,12,13,
...the output is this:
1
2
3
4
5
6
7
8
9
10
11
12
13
What exactly do you want to do?
I'd probably use something like:
while (fscanf(file, "%d%*[, \t\n]", &numbers[i++]))
;
The %d converts a number, and the "%*[, \t\n]" reads (but does not assign) any consecutive run of separators -- which I've defined as commas, spaces, tabs, newlines, though that's fairly trivial to change to whatever you see fit.
fscanf(file, "%d,%d,%d,%d", &n1, &n2, &n3, &n4); but won't work if there are spaces between numbers. This answer shows how to do it (since there aren't library functions for this)
Jerry Coffin's answer is nice, though there are a couple of caveats to watch for:
fscanf returns a (negative) value at the end of the file, so the loop won't terminate properly.
i is incremented even when nothing was read, so it will end up pointing one past the end of the data.
Also, fscanf skips all whitespace (including \t and \n if you leave a space between format parameters.
I'd go for something like this.
int numbers[5000];
int i=0;
while (fscanf(file, "%d %*[,] ", &numbers[i])>0 && i<sizeof(numbers))
{
i++;
}
printf("%d numbers were read.\n", i);
Or if you want to enforce there being a comma between the numbers you can replace the format string with "%d , ".