Missing Lines With fscanf()

Discussion in 'C Programming' started by Psibur, Jun 4, 2004.

  1. Psibur

    Psibur Guest

    Hello, trying to get back into c and was having issue with reading a
    simple text file with an aribtrary # of lines with 3 int's per line,
    with the eventual purpose of putting each int into an element of an
    array (eventually will be other things, but I'm sticking to int's for
    now). I.e.:
    0 1 1
    1 1 1
    2 1 1 etc...

    The problem is it'll read and print all but the last line. Is there
    something I'm forgetting about an End Of Line or EOF with fscanf? If
    it means anything, I'm using gcc on win32 (djgpp) and haven't been
    able to compile it under Linux yet (if there's much difference.) Here
    is the minimalist version of the code with all the fault protection
    and good stuff taken out for brevity:

    #include <stdio.h>

    int main() {
    char buff[BUFSIZ];
    FILE *infile;
    int nums[10];

    infile = fopen("a.dat", "r");
    fscanf(infile, "%d %d %d\n", &nums[0],&nums[1], &nums[2]);
    while (!feof(infile)) {
    /*future home of parsing junk*/
    printf("%d %d %d\n", nums[0], nums[1], nums[2]);
    fscanf(infile, "%d %d %d\n", &nums[0], &nums[1], &nums[2]);
    }
    fclose(infile);

    return 0;
    }

    Much appreciation in advance.
     
    Psibur, Jun 4, 2004
    #1
    1. Advertising

  2. Psibur

    Eric Sosman Guest

    Psibur wrote:
    > Hello, trying to get back into c and was having issue with reading a
    > simple text file with an aribtrary # of lines with 3 int's per line,
    > with the eventual purpose of putting each int into an element of an
    > array [...]
    > The problem is it'll read and print all but the last line. Is there
    > something I'm forgetting about an End Of Line or EOF with fscanf? If
    > it means anything, I'm using gcc on win32 (djgpp) and haven't been
    > able to compile it under Linux yet (if there's much difference.) Here
    > is the minimalist version of the code with all the fault protection
    > and good stuff taken out for brevity:
    >
    > #include <stdio.h>
    >
    > int main() {
    > char buff[BUFSIZ];
    > FILE *infile;
    > int nums[10];
    >
    > infile = fopen("a.dat", "r");
    > fscanf(infile, "%d %d %d\n", &nums[0],&nums[1], &nums[2]);
    > while (!feof(infile)) {
    > /*future home of parsing junk*/
    > printf("%d %d %d\n", nums[0], nums[1], nums[2]);
    > fscanf(infile, "%d %d %d\n", &nums[0], &nums[1], &nums[2]);
    > }
    > fclose(infile);
    >
    > return 0;
    > }


    The two problems I can see are addressed in Questions 12.17
    and 12.2 of the comp.lang.c Frequently Asked Questions (FAQ) list:

    http://www.eskimo.com/~scs/C-faq/top.html

    (To understand what role 12.2 plays here, I think you'd do better
    to study 12.17 first.)

    --
     
    Eric Sosman, Jun 4, 2004
    #2
    1. Advertising

  3. Psibur

    CBFalconer Guest

    Psibur wrote:
    >
    > Hello, trying to get back into c and was having issue with reading a
    > simple text file with an aribtrary # of lines with 3 int's per line,
    > with the eventual purpose of putting each int into an element of an
    > array (eventually will be other things, but I'm sticking to int's for
    > now). I.e.:
    > 0 1 1
    > 1 1 1
    > 2 1 1 etc...
    >
    > The problem is it'll read and print all but the last line. Is there
    > something I'm forgetting about an End Of Line or EOF with fscanf? If
    > it means anything, I'm using gcc on win32 (djgpp) and haven't been
    > able to compile it under Linux yet (if there's much difference.) Here
    > is the minimalist version of the code with all the fault protection
    > and good stuff taken out for brevity:
    >
    > #include <stdio.h>
    >
    > int main() {
    > char buff[BUFSIZ];
    > FILE *infile;
    > int nums[10];
    >
    > infile = fopen("a.dat", "r");
    > fscanf(infile, "%d %d %d\n", &nums[0],&nums[1], &nums[2]);
    > while (!feof(infile)) {
    > /*future home of parsing junk*/
    > printf("%d %d %d\n", nums[0], nums[1], nums[2]);
    > fscanf(infile, "%d %d %d\n", &nums[0], &nums[1], &nums[2]);
    > }
    > fclose(infile);
    >
    > return 0;
    > }


    Try (untested):

    #include <stdio.h>

    int main(void)
    {
    FILE *infile;
    int nums[10];

    if (infile = fopen("a.dat", "r")) {
    while (3 == fscanf(infile, "%d %d %d\n",
    &nums[0], &nums[1], &nums[2])) {
    /*future home of parsing junk*/
    printf("%d %d %d\n", nums[0], nums[1], nums[2]);
    }
    fclose(infile);
    }
    return 0;
    }

    NEVER fail to test the result of an input operation. USUALLY test
    the result of any system call.

    --
    "The most amazing achievement of the computer software industry
    is its continuing cancellation of the steady and staggering
    gains made by the computer hardware industry..." - Petroski
     
    CBFalconer, Jun 4, 2004
    #3
  4. On Fri, 4 Jun 2004, Psibur wrote:

    > Hello, trying to get back into c and was having issue with reading a
    > simple text file with an aribtrary # of lines with 3 int's per line,
    > with the eventual purpose of putting each int into an element of an
    > array (eventually will be other things, but I'm sticking to int's for
    > now). I.e.:
    > 0 1 1
    > 1 1 1
    > 2 1 1 etc...
    >
    > The problem is it'll read and print all but the last line. Is there
    > something I'm forgetting about an End Of Line or EOF with fscanf? If
    > it means anything, I'm using gcc on win32 (djgpp) and haven't been
    > able to compile it under Linux yet (if there's much difference.) Here
    > is the minimalist version of the code with all the fault protection
    > and good stuff taken out for brevity:
    >
    > #include <stdio.h>
    >
    > int main() {
    > char buff[BUFSIZ];
    > FILE *infile;
    > int nums[10];
    >
    > infile = fopen("a.dat", "r");
    > fscanf(infile, "%d %d %d\n", &nums[0],&nums[1], &nums[2]);
    > while (!feof(infile)) {
    > /*future home of parsing junk*/
    > printf("%d %d %d\n", nums[0], nums[1], nums[2]);
    > fscanf(infile, "%d %d %d\n", &nums[0], &nums[1], &nums[2]);
    > }
    > fclose(infile);
    >
    > return 0;
    > }


    The fscanf in the while loop triggers EOF on reading the last line. After
    the last fscanf it goes to the top of the loop, the exit condition is met
    and you leave the loop before printing the last lines.

    I usually use fgets to read a line then parse it. Another option is to get
    the result from fscanf and see how many directives were successful, e.g.

    result = fscanf(infile, "%d %d %d\n", &num[0], &num[1], &num[2]);

    then exit if result != 3. I'd actually use the fscanf in the while
    expression rather than having two calls, e.g.

    while(fscanf(infile, "%d %d %d\n", &num[0], &num[1], &num[2]) == 3) {
    printf("%d %d %d\n", nums[0], nums[1], nums[2]);
    }

    This will work even if the last line does not contain a newline character.
    It will not print out a line that only has 1 or 2 numbers on it. Something
    like:

    1 0 1
    1 2 1
    1 3
    1 2 0

    will quit after the second line is read.

    --
    Send e-mail to: darrell at cs dot toronto dot edu
    Don't send e-mail to
     
    Darrell Grainger, Jun 7, 2004
    #4
  5. Psibur

    Chris Torek Guest

    In article <news:p>
    Darrell Grainger <> writes:
    >I usually use fgets to read a line then parse it.


    (I think this is usually the best approach.)

    >Another option is to get the result from fscanf and see how many
    >directives were successful ...


    This has some pitfalls, including one you ran into here:

    >rather than having two calls, e.g.
    > while(fscanf(infile, "%d %d %d\n", &num[0], &num[1], &num[2]) == 3) {
    > printf("%d %d %d\n", nums[0], nums[1], nums[2]);
    > }
    >This will work even if the last line does not contain a newline character.


    True (assuming the implementation supports such lines in the first
    place), but this:

    >It will not print out a line that only has 1 or 2 numbers on it. Something
    >like:
    >
    >1 0 1
    >1 2 1
    >1 3
    >1 2 0
    >
    >will quit after the second line is read.


    is not the case.

    The problem lies in the scanf engine's interpretation of "white
    space", which disagrees with what most programmers believe is the
    "most reasonable" interpretation.

    The directive sequence "%d %d %d\n" does *not* mean "three
    integers separated by blanks and terminated by a newline." The
    final "\n", for instance, means "as much blank space as possible,
    including any arbitrary number of newlines, and including no newlines."

    The blanks separating the "%d"s *also* mean "as much blank space
    as possible, including any arbitrary number of newlines, and including
    no newlines."

    That means that the third input line -- "1 3\n" -- will be treated
    as the number 1 (first "%d"), one blank (first " "), the number 3
    (second "%d"), one newline (second " ")... and then the fourth input
    line's "1" will be read and converted to satisfy the third "%d". The
    newline in the scanf directive will then match the blank after the
    1, leaving "2 0\n" in the input stream.

    One might attempt to fix this by rewriting the call as:

    while (fscanf(infile, "%d%d%d\n", ...) ...)

    so that there is no " " directive to match the newline after the
    two integers on the third input line ("1 3\n"). But this is no
    help, because %d directives include an implied whitespace directive
    first. "%d%d%d\n" means exactly the same thing to scanf as
    "%d %d %d\n", or even " %d%d \v %d \t\f\r\n"! *All* whitespace
    in format directives is equivalent, and any whitespace is equivalent
    to an arbitrarily long string of whitespace. Most scanf directives
    -- the exceptions are %c and %[ -- have an "implied blank" in front
    of them. A final "\n" in a scanf directive means the same thing as
    a trailing blank, or "\t", or "\f".

    What this all boils down to is that *only* the %c and %[ directives
    give you control over "lines". *All* other scanf conversions will
    behave poorly in any kind of file-parsing or interactive application.
    In other words, these are the only *useful* directives -- unless
    you first read in a line, then use sscanf() to pick it apart. (By
    using sscanf(), you avoid the problem, because you have a tightly
    controlled string in memory, rather than an uncontrolled input
    stream coming from a file or user-at-keyboard or whatnot.)
    --
    In-Real-Life: Chris Torek, Wind River Systems
    Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
    email: forget about it http://web.torek.net/torek/index.html
    Reading email is like searching for food in the garbage, thanks to spammers.
     
    Chris Torek, Jun 11, 2004
    #5
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. cylin
    Replies:
    7
    Views:
    3,973
    Kevin Goodsell
    Sep 19, 2003
  2. Marc Reclaire

    Question about fscanf ..

    Marc Reclaire, Dec 18, 2003, in forum: C++
    Replies:
    2
    Views:
    397
    Karl Heinz Buchegger
    Dec 18, 2003
  3. John Phung

    fscanf equivalent in c++

    John Phung, Apr 6, 2004, in forum: C++
    Replies:
    11
    Views:
    19,364
    Nick Hounsome
    Apr 9, 2004
  4. CJ

    fscanf to read lines from file?

    CJ, May 20, 2004, in forum: C Programming
    Replies:
    2
    Views:
    3,027
    Dan Pop
    May 21, 2004
  5. fscanf reading lines

    , Mar 8, 2006, in forum: C Programming
    Replies:
    7
    Views:
    910
    Fred Kleinschmidt
    Mar 8, 2006
Loading...

Share This Page