questions on ftell and fopen

  • Thread starter subramanian100in
  • Start date
S

subramanian100in

Consider the following program:

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

int main(int argc, char *argv[])
{
if (argc != 2)
{
printf("Usage: <program-name> <text-file>\n");
exit(EXIT_FAILURE);
}

FILE *fp;

if ((fp = fopen(argv[1], "r")) == NULL)
{
printf("Could not open input file :%s:\n", argv[1]);
exit(EXIT_FAILURE);
}

if (fseek(fp, 0L, SEEK_END) == 0)
printf("fseek successful\n");
else
printf("fseek failed\n");

long int size = ftell(fp);

printf("filesize = %ld\n", size);

fclose(fp);

char a[size+1];
a[size] = '\0';

if ((fp = fopen(argv[1], "rb")) == NULL)
{
printf("Could not open input file :%s:\n", argv[1]);
exit(EXIT_FAILURE);
}

fread(a, size, 1, fp);

if (ferror(fp))
{
printf("fread failed\n");
fclose(fp);
exit(EXIT_FAILURE);
}

fclose(fp);

printf("%s", a);

return 0;
}

Suppose the name of the file is tmp.c

I compiled this program with gcc under Redhat Enterprise Linux with
the command

gcc -std=c99 tmp.c

It compiles well. I ran it with the command

$./a.out tmp.c

It runs successfully.

My questions:
1) Is the usage of ftell above correct ?
2) In the second fopen, I have used "rb" for the mode ie I am opening
the text file in binary mode and then using fread. Will this always
work ?
 
S

subramanian100in

Sorry for the inconvenience.

The first question should be
1)Can I use fseek for a text stream as used in this program(though it
seems to work)
 
F

Flash Gordon

Consider the following program:

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

int main(int argc, char *argv[])
{
if (argc != 2)
{
printf("Usage: <program-name> <text-file>\n");
exit(EXIT_FAILURE);
}

FILE *fp;

if ((fp = fopen(argv[1], "r")) == NULL)
{
printf("Could not open input file :%s:\n", argv[1]);
exit(EXIT_FAILURE);
}

if (fseek(fp, 0L, SEEK_END) == 0)
printf("fseek successful\n");
else
printf("fseek failed\n");

long int size = ftell(fp);

printf("filesize = %ld\n", size);

This does not portably determine the size of a file. Read question 19.12
of the comp.lang.c FAQ which you can find at http://c-faq.com/
fclose(fp);

char a[size+1];
a[size] = '\0';

if ((fp = fopen(argv[1], "rb")) == NULL)
{
printf("Could not open input file :%s:\n", argv[1]);
exit(EXIT_FAILURE);
}

fread(a, size, 1, fp);

Note in particular that some common systems use two bytes to indicate
the end of a line, will it count those as 1 or 2 bytes in the result
returned by ftell?
if (ferror(fp))
{
printf("fread failed\n");
fclose(fp);
exit(EXIT_FAILURE);
}

fclose(fp);

printf("%s", a);

return 0;
}

Suppose the name of the file is tmp.c

I compiled this program with gcc under Redhat Enterprise Linux with
the command

gcc -std=c99 tmp.c

It compiles well. I ran it with the command

$./a.out tmp.c

It runs successfully.

My questions:
1) Is the usage of ftell above correct ?

No, it is not guaranteed to return the byte count on text files.
2) In the second fopen, I have used "rb" for the mode ie I am opening
the text file in binary mode and then using fread. Will this always
work ?

I cannot think of a reason for it not to work on *nix, but there is
nothing to stop some system having a text/binary attribute and refusing
to open text files in binary mode or binary files in text mode, on such
a system one of your fopen calls would always fail, which one depending
on the type of the file.

In addition, reading a file in binary mode then printing it out in text
mode could have strange effects. On a Windows system, you would be
likely to find that each end-of-line CRLR read would be printed as
CRCRLF, and on MacOS9 or earlier you would have different, and possibly
stranger, effects.

Also, who says you will have enough memory to lead the entire file? The
system I'm working with frequently has files approaching 2GB (including
text files, and once everyone is on modern file systems we will probably
allow them to grow even larger.
 
M

mark_bluemel

Consider the following program:

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

int main(int argc, char *argv[])
{
if (argc != 2)
{
printf("Usage: <program-name> <text-file>\n");
exit(EXIT_FAILURE);
}

FILE *fp;

if ((fp = fopen(argv[1], "r")) == NULL)
{
printf("Could not open input file :%s:\n", argv[1]);
exit(EXIT_FAILURE);
}

if (fseek(fp, 0L, SEEK_END) == 0)
printf("fseek successful\n");
else
printf("fseek failed\n");

long int size = ftell(fp);

printf("filesize = %ld\n", size);

fclose(fp);

char a[size+1];
a[size] = '\0';

if ((fp = fopen(argv[1], "rb")) == NULL)
{
printf("Could not open input file :%s:\n", argv[1]);
exit(EXIT_FAILURE);
}

fread(a, size, 1, fp);

if (ferror(fp))
{
printf("fread failed\n");
fclose(fp);
exit(EXIT_FAILURE);
}

fclose(fp);

printf("%s", a);

return 0;

}

Suppose the name of the file is tmp.c

I compiled this program with gcc under Redhat Enterprise Linux with
the command

gcc -std=c99 tmp.c

It compiles well. I ran it with the command

$./a.out tmp.c

It runs successfully.

My questions:

FAQ 12.40 et al http://c-faq.com/stdio/textvsbinary.html
1) Is the usage of ftell above correct ?

No - from the FAQ :-
"Text mode translations also affect the apparent size of a file as
it's read. Because the characters read from and written to a file in
text mode do not necessarily match the characters stored in the file
exactly, the size of the file on disk may not always match the number
of characters which can be read from it. Furthermore, for analogous
reasons, the fseek and ftell functions do not necessarily deal in pure
byte offsets from the beginning of the file. (Strictly speaking, in
text mode, the offset values used by fseek and ftell should not be
interpreted at all: a value returned by ftell should only be used as a
later argument to fseek, and only values returned by ftell should be
used as arguments to fseek.)"
2) In the second fopen, I have used "rb" for the mode ie I am opening
the text file in binary mode and then using fread. Will this always
work ?

Yes - read the FAQ.

However reading it into a buffer sized by fseek/ftell on a stream
opened in text mode is asking for trouble.
 
C

CBFalconer

Kindly answer my second question also.

I see no second question. In fact I see no first question. See
the following sig and link.

--
If you want to post a followup via groups.google.com, ensure
you quote enough for the article to make sense. Google is only
an interface to Usenet; it's not Usenet itself. Don't assume
your readers can, or ever will, see any previous articles.
More details at: <http://cfaj.freeshell.org/google/>
 
B

Bill Pursell

Consider the following program:

if ((fp = fopen(argv[1], "r")) == NULL)
{
printf("Could not open input file :%s:\n", argv[1]);
exit(EXIT_FAILURE);
}

Just a note, you may be much happier replacing the printf line with:
perror(argv[1]);
It will print a much more meaningful message. eg:
[tmp]$ ./a.out foo
foo: No such file or directory
[tmp]$ touch foo; chmod 000 foo; ./a.out foo
foo: Permission denied
if (fseek(fp, 0L, SEEK_END) == 0)
printf("fseek successful\n");
else
printf("fseek failed\n");

Again here. Don't use printf, use perror, as it
will tell you the reason for the error.
long int size = ftell(fp);

printf("filesize = %ld\n", size);

This may be a philosophical complaint, but this
is totally bogus. It's like going to the mouth
of a river, computing the distance to the headwaters
and claiming that you can derive the amount of
water in the river from that information. "How
much water is in the river?" is simply a non-sensical
question. There are some files for which it makes sense,
but in the strict sense of the C standard, asking for
the size of a file is simply an absurd question.
Files are streams. If you have a "regular file" (which
is outside the scope of the C standard), then the
correct way to determine its size is system specific.
Since you've said you're on Linux, you should use fstat().

My questions:
1) Is the usage of ftell above correct ?

In a philosophical sense, no.
2) In the second fopen, I have used "rb" for the mode ie I am opening
the text file in binary mode and then using fread. Will this always
work ?

Since you indicate that you ran this on a linux box: consider this
from the documentation:

The mode string can also include the letter ''b'' either as
a last character or as a character between the characters in
any of the two-character strings described above. This is
strictly for compatibility with ANSI X3.159-1989 (''ANSI C'')
and has no effect; the ''b'' is ignored on all POSIX conforming
systems, including Linux.
 
K

Keith Thompson

Bill Pursell said:
Consider the following program:
if ((fp = fopen(argv[1], "r")) == NULL)
{
printf("Could not open input file :%s:\n", argv[1]);
exit(EXIT_FAILURE);
}

Just a note, you may be much happier replacing the printf line with:
perror(argv[1]);
It will print a much more meaningful message. eg:
[tmp]$ ./a.out foo
foo: No such file or directory
[tmp]$ touch foo; chmod 000 foo; ./a.out foo
foo: Permission denied

It's likely to do so on most systems, but the standard doesn't
actually guarantee that a failing fopen() sets errno.

[...]
This may be a philosophical complaint, but this
is totally bogus. It's like going to the mouth
of a river, computing the distance to the headwaters
and claiming that you can derive the amount of
water in the river from that information. "How
much water is in the river?" is simply a non-sensical
question.

Not a great example. "How much water is in the river?" is a sensible
question with a definite answer; computing the distance to the
headwaters just isn't a good way to measure it.

The "size" of a file, on the other hand, may or may not be a
meaningful concept, depending on the file and the OS. A plain file on
a Unix-like system does have a meaningful size, and the fseek/ftell
trick should give it to you accurately (if it doesn't exceed LONG_MAX
bytes); calling fgetc() in a loop and counting characters will give
you the same result, but much more slowly.

But for non-plain files, or for files on other systems, these
techniques may give you inconsistent results, or may not work at all.
The C standard wisely doesn't require Unix-like semantics, so there is
no portable way to determine the "size" of a file.

If you want to know the size of a file, first ask yourself exactly
what that means, and why you want to know it.
 
B

Bill Pursell

Bill Pursell said:
Consider the following program: <some code snipped>
if ((fp = fopen(argv[1], "r")) == NULL)
{
printf("Could not open input file :%s:\n", argv[1]);
exit(EXIT_FAILURE);
}
Just a note, you may be much happier replacing the printf line with:
perror(argv[1]);
It will print a much more meaningful message. eg:
[tmp]$ ./a.out foo
foo: No such file or directory
[tmp]$ touch foo; chmod 000 foo; ./a.out foo
foo: Permission denied

It's likely to do so on most systems, but the standard doesn't
actually guarantee that a failing fopen() sets errno.

True. However, it strikes me that it is NOT the
programmer's responsibility to account for inadequacies
of the implementation.
 
F

Flash Gordon

Bill Pursell wrote, On 04/03/07 06:57:
True. However, it strikes me that it is NOT the
programmer's responsibility to account for inadequacies
of the implementation.

Wrong IMHO. It is the programmer's responsibility to produce software
that works correctly on whatever system the customer wants even if the C
implementation on that system is completely useless. Of course, if you
are prepared to pay compensation for the loss of one of my companies
biggest customers because you think we should not allow for the
inadequacies of their chosen system I will happily stop jumping through
the required hoops to support it.
 
B

Bill Pursell

Bill Pursell wrote, On 04/03/07 06:57:



Wrong IMHO. It is the programmer's responsibility to produce software
that works correctly on whatever system the customer wants even if the C
implementation on that system is completely useless.

So how do you deal with it? Do you write a wrapper like the
following around every libc function that the standard doesn't
require to set errno? It seems rather kludgy.


#include <stdio.h>

void
report_fopen_error( const char * path )
{
#ifdef FOPEN_SETS_ERRNO
perror( path );
#else
const char *format = "unable to open %s: reason unkown. %s\n";
const char *smarmy_remark = "Upgrade your box.";
fprintf( stderr, format, path, smarmy_remark);
#endif
}
 
B

Ben Pfaff

Bill Pursell said:
Bill Pursell wrote, On 04/03/07 06:57:



Wrong IMHO. It is the programmer's responsibility to produce software
that works correctly on whatever system the customer wants even if the C
implementation on that system is completely useless.

So how do you deal with it? [...]

You can save a copy of errno and set errno to 0 before calling
fopen, then on fopen failure only print a message based on errno
if errno is nonzero.

Most code that I've seen doesn't try this hard.
 
K

Keith Thompson

Bill Pursell said:
Bill Pursell said:
On Mar 2, 5:52 am, "(e-mail address removed), India"
Consider the following program:
<some code snipped>
if ((fp = fopen(argv[1], "r")) == NULL)
{
printf("Could not open input file :%s:\n", argv[1]);
exit(EXIT_FAILURE);
}
Just a note, you may be much happier replacing the printf line with:
perror(argv[1]);
It will print a much more meaningful message. eg:
[tmp]$ ./a.out foo
foo: No such file or directory
[tmp]$ touch foo; chmod 000 foo; ./a.out foo
foo: Permission denied

It's likely to do so on most systems, but the standard doesn't
actually guarantee that a failing fopen() sets errno.

True. However, it strikes me that it is NOT the
programmer's responsibility to account for inadequacies
of the implementation.

Yes, it is. Whose responsibility do you think it is?

A standard is a contract between the implementer and the programmer.
If an implementer claims his compiler conforms to the C standard, the
programmer may reasonably rely on that claim and make use of any
features guaranteed by the standard. If the programmer relies on
something *not* guaranteed by the standard (in this case, that fopen()
sets errno on failure), and the program misbehaves as a result, who is
responsible for the failure?

On the other hand, another standard might provide additional
guarantees. POSIX, for example, requires fopen() to set errno on
failure. If an implementer claims conformance to POSIX as well as to
the C standard, then the programmer may reasonably rely on that, as
long as he doesn't claim or expect that his program will work properly
on a system that doesn't conform to POSIX. (Relying on POSIX
conformance is often perfectly reasonable, but it's not an assumption
we can make in this newsgroup, at least not without explicitly stating
it.)
 
B

Bill Pursell

So how do you deal with it? [...]

You can save a copy of errno and set errno to 0 before calling
fopen, then on fopen failure only print a message based on errno
if errno is nonzero.

I don't see that the standard guarantees that fopen will
not modify errno if it is succesful. Is there such a
guarantee? Or that fopen() won't fail and set errno
to a totally unrelated non-zero value, and give me:
foo: Invalid cross-device link

It seems to me that you have to rely on the implementation
for something, or you'll just go insane. I'll stick
with simply calling perror() (or err() or error()) but
I'm not going to reimburse Flash if his customer leaves. :)
 
B

Ben Pfaff

Bill Pursell said:
Bill Pursell said:
On Mar 4, 11:13 am, Flash Gordon wrote:
Bill Pursell wrote, On 04/03/07 06:57:
On Mar 3, 8:06 pm, Keith Thompson wrote:
It's likely to do so on most systems, but the standard doesn't
actually guarantee that a failing fopen() sets errno.
True. However, it strikes me that it is NOT the
programmer's responsibility to account for inadequacies
of the implementation.
Wrong IMHO. It is the programmer's responsibility to produce software
that works correctly on whatever system the customer wants even if the C
implementation on that system is completely useless.
So how do you deal with it? [...]

You can save a copy of errno and set errno to 0 before calling
fopen, then on fopen failure only print a message based on errno
if errno is nonzero.

I don't see that the standard guarantees that fopen will
not modify errno if it is succesful. Is there such a
guarantee?

No. But: if fopen is successful, then you don't care what errno
is set to, or not set to.
Or that fopen() won't fail and set errno to a totally unrelated
non-zero value, and give me: foo: Invalid cross-device link

That's an issue that standard C gives you no way to avoid.
 
B

Bill Pursell

Bill Pursell said:
On Mar 2, 5:52 am, "(e-mail address removed), India"
Consider the following program:
<some code snipped>
if ((fp = fopen(argv[1], "r")) == NULL)
{
printf("Could not open input file :%s:\n", argv[1]);
exit(EXIT_FAILURE);
}
Just a note, you may be much happier replacing the printf line with:
perror(argv[1]);
It will print a much more meaningful message. eg:
[tmp]$ ./a.out foo
foo: No such file or directory
[tmp]$ touch foo; chmod 000 foo; ./a.out foo
foo: Permission denied
It's likely to do so on most systems, but the standard doesn't
actually guarantee that a failing fopen() sets errno.
True. However, it strikes me that it is NOT the
programmer's responsibility to account for inadequacies
of the implementation.

Yes, it is. Whose responsibility do you think it is?

A standard is a contract between the implementer and the programmer.
If an implementer claims his compiler conforms to the C standard, the
programmer may reasonably rely on that claim and make use of any
features guaranteed by the standard. If the programmer relies on
something *not* guaranteed by the standard (in this case, that fopen()
sets errno on failure), and the program misbehaves as a result, who is
responsible for the failure?

We're not talking about a failure here, we're talking about
the quality of an error message. On a good implementation,
perror() will generate a reasonable error message. On
a poor implementation, the error messsage will be less
helpful. Is it any more helpful to output:
"operation failed, and this implementation is so broken
that I can't tell you why." than it is to output a message
based on an incorrect value of errno? (well, yeah, I
guess it is...but not by much.)

I suppose I'll raise my standards and make a point
to only claim POSIX conformance for my code. Time
to remove -ansi from CFLAGS, I guess...
 
K

Keith Thompson

Bill Pursell said:
Bill Pursell said:
On Mar 4, 11:13 am, Flash Gordon wrote:
Bill Pursell wrote, On 04/03/07 06:57:
On Mar 3, 8:06 pm, Keith Thompson wrote:
It's likely to do so on most systems, but the standard doesn't
actually guarantee that a failing fopen() sets errno.
True. However, it strikes me that it is NOT the
programmer's responsibility to account for inadequacies
of the implementation.
Wrong IMHO. It is the programmer's responsibility to produce software
that works correctly on whatever system the customer wants even if the C
implementation on that system is completely useless.
So how do you deal with it? [...]

You can save a copy of errno and set errno to 0 before calling
fopen, then on fopen failure only print a message based on errno
if errno is nonzero.

I don't see that the standard guarantees that fopen will
not modify errno if it is succesful. Is there such a
guarantee? Or that fopen() won't fail and set errno
to a totally unrelated non-zero value, and give me:
foo: Invalid cross-device link

No, there is no such guarantee. In fact, setting errno to some
non-zero value on success is fairly likely, which is why you should
examine errno only after you know something has failed.

For example, suppose fopen() calls other lower-level functions
internally, and those other functions set errno if they fail:

FILE *fopen(/* blah blah */)
{
try_something();
/* fails, sets errno to EFOOBAR */
...
try_something_else();
/* that worked, leaves errno alone, fopen() succeeds */
return some_result;
}
It seems to me that you have to rely on the implementation
for something, or you'll just go insane. I'll stick
with simply calling perror() (or err() or error()) but
I'm not going to reimburse Flash if his customer leaves. :)

In this particular case, the worst you can expect is a meaningless
error message. But as I wrote in another response, what you're
probably doing here is depending on POSIX conformance, which is
perfectly ok as long as you're aware that that's what you're depending
on.
 
G

Gordon Burditt

It's likely to do so on most systems, but the standard doesn't
Since the standard doesn't guarantee that a failing fopen() does *NOT*
set errno, you should output it as part of an error message.
We're not talking about a failure here, we're talking about
the quality of an error message. On a good implementation,
perror() will generate a reasonable error message. On
a poor implementation, the error messsage will be less
helpful. Is it any more helpful to output:
"operation failed, and this implementation is so broken
that I can't tell you why." than it is to output a message
based on an incorrect value of errno? (well, yeah, I
guess it is...but not by much.)

If it is known that fopen() will set errno to ENOMEM if it runs out
of memory, and ENOTTY (misleading at best) if opening the file
fails, then printing "operation failed: Not a typewriter" is an
improvement over a generic "operation failed and I don't have a
clue why", because in at least one circumstance (running out of
memory) it provides a better answer.
 

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

Similar Threads

URGENT 1
Linux: using "clone3" and "waitid" 0
Question about change of "fp" in function "fseek"and "ftell" 3
seg fault 76
fseek 17
Command Line Arguments 0
Help with EXT3 Filesystem work 1
error 28

Members online

No members online now.

Forum statistics

Threads
473,767
Messages
2,569,570
Members
45,045
Latest member
DRCM

Latest Threads

Top