no error by fscanf on reading from output file

  • Thread starter V.Subramanian, India
  • Start date
V

V.Subramanian, India

This question is for learning purpose only.

Consider the program sample.c :

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

int main()
{
errno = 0;

FILE *fp = fopen("data.txt", "w");

if (fp == NULL)
{
fprintf(stderr,
"Could not open input file - data.txt\n%s\n",
strerror(errno));

return EXIT_FAILURE;
}

if (fprintf(fp, "test data") < 1)
{
fprintf(stderr,
"could not write message into data.txt\n");

return EXIT_FAILURE;
}

fflush(fp);

int a;
int b;

errno = 0;

// I deliberately try to read from the file
// opened in 'write' mode
if (fscanf(fp, "%d%d", &a, &b) == 2)
{
fprintf(stdout,
"a = %d, b= %d\n",
a,
b);
fflush(stdout);
fclose(fp);
return EXIT_SUCCESS;
}

int err = errno;

if (ferror(fp))
{
fprintf(stderr,
"Error encountered while reading input"
" from file\n%s\n",
strerror(err));
fclose(fp);

return EXIT_FAILURE;
}

fclose(fp);

return EXIT_SUCCESS;
}

In this program I open a file 'data.txt' in 'write' mode.
Subsequently, I write something into this file. Then I read
from this file using 'fscanf'. This operation should fail.
But when I test the error indicator for the stream by
calling 'ferror(fp)', the statements inside the
if (ferror(fp))
{
}
are NOT executed.
Does this mean that error inidcator for the stream is not set
by 'fscanf' ? Isn't the operation 'reading from a file
opened in "write" mode' an error ?

I am unable to understand this.

Kindly explain.

Thanks
V.Subramanian
 
V

V.Subramanian, India

I forgot to add to the OP that I compiled this program under RedHat
Linux with gcc 3.4.3 as
gcc -std=c99 -pedantic -Wall -Wextra sample.c

There is no warning or error.
 
I

Ian Collins

This question is for learning purpose only.

Consider the program sample.c :

#include<stdlib.h>
#include<stdio.h>
#include<errno.h>
#include<string.h>

int main()
{
errno = 0;

FILE *fp = fopen("data.txt", "w");

if (fp == NULL)
{
fprintf(stderr,
"Could not open input file - data.txt\n%s\n",
strerror(errno));

return EXIT_FAILURE;
}

if (fprintf(fp, "test data")< 1)
{
fprintf(stderr,
"could not write message into data.txt\n");

return EXIT_FAILURE;
}

fflush(fp);

int a;
int b;

errno = 0;

// I deliberately try to read from the file
// opened in 'write' mode
if (fscanf(fp, "%d%d",&a,&b) == 2)

You don't test the return of fscanf. Do so and see what happens.
{
fprintf(stdout,
"a = %d, b= %d\n",
a,
b);
fflush(stdout);
fclose(fp);
return EXIT_SUCCESS;
}

int err = errno;

if (ferror(fp))
{
fprintf(stderr,
"Error encountered while reading input"
" from file\n%s\n",
strerror(err));
fclose(fp);

return EXIT_FAILURE;
}

fclose(fp);

return EXIT_SUCCESS;
}

In this program I open a file 'data.txt' in 'write' mode.
Subsequently, I write something into this file. Then I read
from this file using 'fscanf'. This operation should fail.

But to fail to check!
But when I test the error indicator for the stream by
calling 'ferror(fp)', the statements inside the
if (ferror(fp))
{
}
are NOT executed.
Does this mean that error inidcator for the stream is not set
by 'fscanf' ? Isn't the operation 'reading from a file
opened in "write" mode' an error ?

No, because the FILE* argument isn't a valid input stream. There's
nothing wrong with the stream, you are just using it wrong.
I am unable to understand this.

Try opening the stream "r+" and then you will see an error condition
occur on the stream.
 
I

Ike Naar

// opened in 'write' mode
if (fscanf(fp, "%d%d", &a, &b) == 2)
{
fprintf(stdout,
"a = %d, b= %d\n",
a,
b);
fflush(stdout);
fclose(fp);
return EXIT_SUCCESS;
}

[snip]

In this program I open a file 'data.txt' in 'write' mode.
Subsequently, I write something into this file. Then I read
from this file using 'fscanf'. This operation should fail.

fscanf does fail (this can be detected by observing that it returns -1).
But fscanf does not read anything from fp (it isn't allowed to
because fp is not in 'read' mode), nothing happens to
the state of fp.
But when I test the error indicator for the stream by
calling 'ferror(fp)', the statements inside the
if (ferror(fp))
{
}
are NOT executed.
Does this mean that error inidcator for the stream is not set
by 'fscanf' ? Isn't the operation 'reading from a file
opened in "write" mode' an error ?

The operation never happes, the stream is not touched.
 
E

Eric Sosman

Yes he does, but he does nothing if the test fails.

Perhaps your newsreader has lopped off part of the O.P.'s code.
In the version I see, the "equals two" case goes down one path and
the "else" does something entirely different. Lightly edited:
 
I

Ian Collins

Perhaps your newsreader has lopped off part of the O.P.'s code.
In the version I see, the "equals two" case goes down one path and
the "else" does something entirely different. Lightly edited:

Entirely different in the sense that it doesn't check why fscanf failed.
 
B

Ben Bacarisse

Ian Collins said:
Entirely different in the sense that it doesn't check why fscanf
failed.

I think it does -- at least it tries to. The code fails because fp is
an output stream, and all tests to see why fscanf failed will be useless
on an output stream. The pattern used, though, is reasonable: test for
success (that fscanf reads the number of items required) and then test
for errors on the stream and for EOF.

The only thing that could be added would be to save the return value and
report how many items matched or failed to match. I tend to do this
because it simplifies the EOF test, but that is a small matter of style.

It might short-circuit the discussion if you say what it is you think
the OP should be testing for.
 
I

Ian Collins

I think it does -- at least it tries to. The code fails because fp is
an output stream, and all tests to see why fscanf failed will be useless
on an output stream. The pattern used, though, is reasonable: test for
success (that fscanf reads the number of items required) and then test
for errors on the stream and for EOF.

The only thing that could be added would be to save the return value and
report how many items matched or failed to match. I tend to do this
because it simplifies the EOF test, but that is a small matter of style.

I guess I'm used to the POSIX behaviour where fscanf will set errno on
failure. In this case, EOF will be returned and errno will be set to
EBADF to indicate the file has not been opened for reading.
 
C

Carlo Dapor

This question is for learning purpose only.

Consider the program sample.c :

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

int main()
{
        errno = 0;

        FILE *fp = fopen("data.txt", "w");

        if (fp == NULL)
        {
                fprintf(stderr,
                        "Could not open input file - data.txt\n%s\n",
                        strerror(errno));

                return EXIT_FAILURE;
        }

        if (fprintf(fp, "test data") < 1)
        {
                fprintf(stderr,
                        "could not write message into data.txt\n");

                return EXIT_FAILURE;
        }

        fflush(fp);

        int a;
        int b;

        errno = 0;

        // I deliberately try to read from the file
        // opened in 'write' mode
        if (fscanf(fp, "%d%d", &a, &b) == 2)
        {
                fprintf(stdout,
                        "a = %d, b= %d\n",
                        a,
                        b);
                fflush(stdout);
                fclose(fp);
                return EXIT_SUCCESS;
        }

        int err = errno;

        if (ferror(fp))
        {
                fprintf(stderr,
                         "Error encountered while reading input"
                         " from file\n%s\n",
                         strerror(err));
                fclose(fp);

                return EXIT_FAILURE;
        }

        fclose(fp);

        return EXIT_SUCCESS;

}

In this program I open a file 'data.txt' in 'write' mode.
Subsequently, I write something into this file. Then I read
from this file using 'fscanf'. This operation should fail.
But when I test the error indicator for the stream by
calling 'ferror(fp)', the statements inside the
        if (ferror(fp))
        {
         }
are NOT executed.
Does this mean that error inidcator for the stream is not set
by 'fscanf' ? Isn't the operation 'reading from a file
opened in "write" mode' an error ?

I am unable to understand this.

Kindly explain.

Thanks
V.Subramanian

Hello V. Subramanian


I also learned a few things, thanks to your posting.
The results are from Mac OS X 10.6.8, they may not apply to RedHat
Linux.

Allow me to point out some observations.

1. fscanf's behaviour

What I take away is that fscanf(3) does not set errno, whether the
call succeeds or not.
Modifying your original source code, errno is always -234 below.
Calling ferror(fp) after fscanf has no effect.

2. Format used

You assumed that two numbers are adjacent ("%d%d").
This is rather misleading.
Say the input were 193.
Should that be read as "1" followed by "93" (case 1), or "19" followed
by "3" (case 2).
The solution is to specify "%1d%2d" for case 1 and "%2d%1d" for case
2.
Another solution is to separate the numbers with a white-space, like
below.

3. Writing and reading to/from the same file

As pointed out, you must create the file with "r+" or "w+".

4. File position

After writing to the file, you are at the end of the file.
If you want to read from it, you must re-position the reader to the
beginning, with fseek or rewind.
Or you could call fclose(fp) followed by fopen(...).

Here's the code:


#include <stdlib.h>
#include <stdio.h>
#include <errno.h>


int
main (int argc, char *argv[])
{
int a, b, count = -1, status = EXIT_SUCCESS;
FILE *fp = fopen ("data.txt", "w+");

fprintf (fp, "23 57"); // valid input
//fprintf (fp, "no number"); // invalid input

if (0 != errno) {
fprintf (stderr, "Could not write data: '%s'\n", strerror
(errno));

return EXIT_FAILURE;
}

// re-set the position to the beginning
rewind (fp);

// unfortunately, errno is not set, whether fscanf succeeds or
not!
errno = -234;
count = fscanf (fp, "%d %d", &a, &b);

if (2 == count) {
fprintf (stdout, "a=%d, b=%d, errno=%d\n", a, b, errno);
}
else {
fprintf (stderr, "Expected 2 tokens, got %d; errno=%d\n",
count, errno);
status = EXIT_FAILURE;
}

fclose (fp);
return status;
}


Regards,
 
I

Ian Collins

I also learned a few things, thanks to your posting.
The results are from Mac OS X 10.6.8, they may not apply to RedHat
Linux.

Allow me to point out some observations.

1. fscanf's behaviour

What I take away is that fscanf(3) does not set errno, whether the
call succeeds or not.

It isn't required to set errno by the C standard, but it is on a POSIX
compliant system. So it should set errno on both Linux and OSX.
 
V

V.Subramanian, India

You don't test the return of fscanf. Do so and see what happens.








But to fail to check!


No, because the FILE* argument isn't a valid input stream. There's
nothing wrong with the stream, you are just using it wrong.


Try opening the stream "r+" and then you will see an error condition
occur on the stream.


Does 'fscanf' set the error indicator of a stream on error while
reading the stream ? If so, ferror(fp) should succeed and the contents
inside 'if (ferror(fp)) {...}' should be executed. Kindly provide a
code for the above scenario. I am unable to come up with this code. I
tried the following:

In the original program mentioned in the OP, I just modified the mode
to "r+" and also "w+" without any other code change; but in both cases
the ferror(fp) part was not executed.

I am compiling the program with the following options:
gcc -std=c99 -pedantic -Wall -Wextra sample.c

I thought, the above options use Standard C and not POSIX. Is it
correct ?

Please help me understand under what circumstances, if at all, fscanf
sets the error indicator for a stream. If it doesn't, I NEVER have to
do the check 'if (ferror(fp)) {...}' after a call to fscanf. Am I
correct ?.

Thanks
V.Subramanian
 
I

Ike Naar

2. Format used

You assumed that two numbers are adjacent ("%d%d").

No. "%d%d" is a valid format for reading two separated integers;
it will skip initial whitespace before the first integer, read the
first integer, skip whitespace beteeen the first and the second
integer, and read the second integer.
This is rather misleading.
Say the input were 193.
Should that be read as "1" followed by "93" (case 1), or "19" followed
by "3" (case 2).

Neither; it will read the first number as 193 (assuming the next
character after "193" does not extend that number to a larger valid
integer). Then any separating whitespace is skipped, and a second
integer will be read. If there is no second integer in the input
stream, fscanf will still read the first integer as 193, then return
1 to indicate that only one item was read.
The solution is to specify "%1d%2d" for case 1 and "%2d%1d" for case
2.

In general you do not know beforehand how large the numbers in the
input stream will be, so you cannot predict the number of digits
to read for the first number. Separating the numbers by whitespace
is often the better way.
 
A

Alan Curry

Does 'fscanf' set the error indicator of a stream on error while
reading the stream ? If so, ferror(fp) should succeed and the contents
inside 'if (ferror(fp)) {...}' should be executed. Kindly provide a
code for the above scenario. I am unable to come up with this code. I
tried the following:

It's nice that you want to test your error handling code path, but injecting
an error that will be reported by ferror() is not easy. ferror() is the
indicator of I/O errors, which usually have a cause that exists outside your
program, like a hard disk with bad sectors.

Check the return value of fscanf first. If it returned EOF, then you can call
ferror() to distinguish between a normal end-of-file condition and an I/O
error. In all other cases, ferror() won't be useful.
 
I

Ian Collins

It's nice that you want to test your error handling code path, but injecting
an error that will be reported by ferror() is not easy. ferror() is the
indicator of I/O errors, which usually have a cause that exists outside your
program, like a hard disk with bad sectors.

Check the return value of fscanf first. If it returned EOF, then you can call
ferror() to distinguish between a normal end-of-file condition and an I/O
error. In all other cases, ferror() won't be useful.

It's unfortunate that without the additional POSIX requirement, there
isn't a standard way to detect the error condition (invalid operation on
the stream) in the original example. I guess the best option is to call
feof() on the stream, at least that way you will be able to
differentiate between an end of file condition and an error.
 
N

Nick Keighley

On 10/30/11 07:59 PM, V.Subramanian, India wrote:
No, because the FILE* argument isn't a valid input stream.  There's
nothing wrong with the stream, you are just using it wrong.
Try opening the stream "r+" and then you will see an error condition
occur on the stream.

it's usual to snip sigs (the bit after a "-- ")

I am compiling the program with the following options:
gcc -std=c99 -pedantic -Wall -Wextra sample.c

I thought, the above options use Standard C and not POSIX. Is it
correct ?

To The Best Of My Knowledge flags to gcc specify the behaviour of gcc
(ie. of the compiler), whilst fscanf() is part of the library. gcc is
actually independent of the library and uses whatever is provided by
the system (there are semi-portable versions of the c library). hence
on a Posix compliant system you get a Posix compiant C library.

Note the C standard only specifies which standdard functions /must/
set errno. It doesn't say that others can't. (pedant point: I'm sure
there are a few library functions that must not set errno).
 
B

Ben Bacarisse

It's nice that you want to test your error handling code path, but injecting
an error that will be reported by ferror() is not easy.

True. I do it with freopen sometimes in conjunction with fwide. It's
not at all portable, but where it works it's great to be able cause IO
fails at any desired test point.

To break an input stream, re-open it with mode "w", and vice-versa. If
the stream is read-write, re-open it read-write but set the stream to be
wide oriented (or byte oriented if you are really using wide stream).

<snip>
 
K

Keith Thompson

Nick Keighley said:
Note the C standard only specifies which standdard functions /must/
set errno. It doesn't say that others can't. (pedant point: I'm sure
there are a few library functions that must not set errno).
[...]

Actually, I don't think there are any.

C99 7.5p3:

The value of errno is zero at program startup, but is never set
to zero by any library function. The value of errno may be set
to nonzero by a library function call whether or not there is
an error, provided the use of errno is not documented in the
description of the function in this International Standard.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,734
Messages
2,569,441
Members
44,832
Latest member
GlennSmall

Latest Threads

Top