Complete.Org: Mailing Lists: Archives: discussion: March 2000:
[aclug-L] Re: Weekly C quiz
Home

[aclug-L] Re: Weekly C quiz

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: discussion@xxxxxxxxx
Subject: [aclug-L] Re: Weekly C quiz
From: Tom Hull <thull@xxxxxxxxxxx>
Date: Mon, 13 Mar 2000 12:33:20 -0600
Reply-to: discussion@xxxxxxxxx

John Reinke wrote:
> 
> Okay. I went back and looked at my code. What I had submitted previously, I
> had in fact tested, but used an old executable. I forgot to recompile it
> first. :-)
> 
> Here's the version that worked, unfortunately it's now a whopping two lines:
> 
> while (fscanf(in_file, "%19[^\n]%*[^\n]", buffer) && !feof(in_file))
> {
>     fscanf(in_file, "%*[\n]");
>     ...
> }

Comments below, following the test program.

> For anyone who wants to know, here's how it works:
> 
> The %[] is like a %s, only you specify the characters or character ranges
> (ie. A-Z) you want in the brackets. It stops filling the string with the
> first character that does not match. If you preceed the character(s) with a
> ^, it will only take characters that DON'T match. The * means it should
> look for whatevery type is specified, and discard it. In the case where
> there is a number after the percent sign, that is the maximum number of
> character to read. You have to make sure buffer contains an additional byte
> for the null terminator, however.
> 
> The return value for fscanf is the number of arguments successfully read,
> not including the discarded values from * format strings. So, it should
> normally be one for the fscanf with the string buffer. At least, that's
> what it is supposed to do. I had to include the feof() call - even though
> no value should be getting read by the fscanf after EOF is encountered - to
> get the condition to fail.

The feof() call is unnecessary and causes a failure. You should be checking
(fscanf() == 1), since fscanf() returns -1 on EOF, and a return of 0 means
fscanf() is hosed and won't advance further.

> I usually use fgets, but the original problem sounded like a case where a
> format string could come in handy for compact (although not pretty) code,
> so I went with fscanf.
> 
> Here's the program I used for testing, I just recompiled and tested it one
> more time. It works.

Sort of.

> #include <stdio.h>
> #include <stdlib.h>
> 
> int main(int argc, char *argv[])
> {
>     FILE *in_file = NULL;
>     char buffer[20];
> 
>     if (argc != 2)
>     {
>         fprintf(stderr, "usage: %s filename\n", argv[0]);
>         exit (1);
>     }
>     if (!(in_file = fopen(argv[1], "r")))
>     {
>         fprintf(stderr, "error opening %s\n", argv[1]);
>         exit (1);
>     }
> 
>     while (fscanf(in_file, "%19[^\n]%*[^\n]", buffer) && !feof(in_file))
>     {
>         fscanf(in_file, "%*[\n]");
>         printf("%s\n", buffer);
>     }
> 
>     fclose(in_file);
>     return 0;
> }

Sorry to be a nag on this, but this code still fails in two cases:

 1) If the first character of a file is '\n', the first fscanf() call returns 0,
    the loop breaks, and you're prematurely done.
 2) If the last line of the file does not contain a '\n', fscanf() returns 1, 
but
    the feof() call returns true, so the loop breaks without printing buffer.

It also suppresses empty lines in the middle of the file, which doesn't seem 
like
a good idea to me, but then the original problem of truncating long lines on 
read
didn't strike me as a good idea, either. I'd just use a big buffer (e.g., 
[5120]),
and fail on overflow (if strchr(buffer, '\n') == 0), on the theory that anything
with longer lines isn't really line-oriented data anyway. Another option would 
be
to grow your buffer on overflow. But just throwing away data because it doesn't
fit your expectations seems like a bad idea.

What strikes me as curious is that the only time I ever see fscanf() used is in
introductory comp sci texts, presumably to provide an elegant complement to the
first introduction of fprintf(). However, fscanf() is useless in the real world,
for while it superficially resembles fprintf(), this ignores the basic fact that
the inputs to fprintf() are well known whereas the inputs to fscanf() are not
well known: fscanf() is very fragile in dealing with bad input. The real world,
however, is full of bad input, and that's the first thing good programmers 
learn.

> >I wrote:
> >>
> >> Okay, here's the one line answer:
> >>
> >> while (fscanf(in_file, "%*[\n]%200[^\n]%*[^\n]", buffer) && !feof(in_file))
> 
> Tom wrote:
> >This looks like it will fail if the first character in in_file is not \n.
> >It also looks like it will fail a last line which does not end in newline,
> >since EOF will have been encountered in filling up buffer or discarding
> >the overflow.
> >...
> >I tried writing a test program using the above fscanf(), and it failed as
> >I expected. Moreover, I couldn't figure out any way to fix it.
> >...
> >Which just goes to remind
> >me that in >20 years of C programming, I've never written a program which
> >actually used fscanf(), for the basic reason that I've never understood
> >how it re-synchs on erroneous input. (I've always suspected that it don't.)
> >...

-- 
/*
 *  Tom Hull * thull@xxxxxxxxxxx * http://www.ocston.org/~thull/
 */

-- This is the discussion@xxxxxxxxx list.  To unsubscribe,
visit http://tmp2.complete.org/cgi-bin/listargate-aclug.cgi


[Prev in Thread] Current Thread [Next in Thread]