fgets not doing as I expect

Discussion in 'C Programming' started by sandeep, Jun 2, 2010.

  1. sandeep

    sandeep Guest

    I wanted to limit how many chars I read in, but the code below instead of
    stopping at
    7 chars will happily read 30 or 40 car lines and spit them out. Not what
    I expected.

    #include<stdio.h>
    main(){
    char line [4096];

    while (NULL!=fgets(line,8,stdin))
    {
    printf("%s",line);
    }

    return 0;
    }
     
    sandeep, Jun 2, 2010
    #1
    1. Advertising

  2. sandeep

    Eric Sosman Guest

    On 6/2/2010 2:29 PM, sandeep wrote:
    > I wanted to limit how many chars I read in, but the code below instead of
    > stopping at
    > 7 chars will happily read 30 or 40 car lines and spit them out. Not what
    > I expected.
    >
    > #include<stdio.h>
    > main(){
    > char line [4096];
    >
    > while (NULL!=fgets(line,8,stdin))
    > {
    > printf("%s",line);
    > }
    >
    > return 0;
    > }


    Your code reads the first seven characters, prints them,
    goes back and reads the next seven, prints them, goes back and
    reads the next seven, ... The fgets() function will stop for
    three reasons: (1) it reads and stores a '\n', (2) it fills
    the buffer, or (3) there's no more input to read (end-of-input
    or I/O error). In cases (1) and (2), the next fgets() will
    resume where the first one left off -- for example, with the
    eighth character of a 30-character line.

    --
    Eric Sosman
    lid
     
    Eric Sosman, Jun 2, 2010
    #2
    1. Advertising

  3. sandeep

    sandeep Guest

    Eric Sosman writes:
    > On 6/2/2010 2:29 PM, sandeep wrote:
    >> I wanted to limit how many chars I read in, but the code below instead
    >> of stopping at
    >> 7 chars will happily read 30 or 40 car lines and spit them out. Not
    >> what I expected.
    >>
    >> #include<stdio.h>
    >> main(){
    >> char line [4096];
    >>
    >> while (NULL!=fgets(line,8,stdin))
    >> {
    >> printf("%s",line);
    >> }
    >>
    >> return 0;
    >> }

    >
    > Your code reads the first seven characters, prints them,
    > goes back and reads the next seven, prints them, goes back and reads the
    > next seven, ... The fgets() function will stop for three reasons: (1)
    > it reads and stores a '\n', (2) it fills the buffer, or (3) there's no
    > more input to read (end-of-input or I/O error). In cases (1) and (2),
    > the next fgets() will resume where the first one left off -- for
    > example, with the eighth character of a 30-character line.
    >
    > --
    > Eric Sosman
    > lid


    I expected that if I typed 123456789 I would get 1234567 out.
    I threw in a \n in the printf and it now makes some sense.

    Still not behaving how I hoped having the 89 spill over is not what I
    wanted, just wanted a nice safe way of inputing size limited fields.
     
    sandeep, Jun 2, 2010
    #3
  4. sandeep

    sandeep Guest

    Richard Heathfield writes:

    > sandeep wrote:
    >
    > <snip>
    >
    >> I expected that if I typed 123456789 I would get 1234567 out. I threw
    >> in a \n in the printf and it now makes some sense.
    >>
    >> Still not behaving how I hoped having the 89 spill over is not what I
    >> wanted, just wanted a nice safe way of inputing size limited fields.

    >
    > You can do that easily enough if you want:
    >
    > char line[1024] = "";
    > char safe[8] = "";
    >
    > while(fgets(line, 8, stdin) != NULL)
    > {
    > printf("%s\n", line);
    >
    > strcpy(safe, line); /* if required for further processing */
    >
    > /* discard rest of overly long lines */ while(strchr(line, '\n') !=
    > NULL &&
    > fgets(line, sizeof line, stdin) != NULL)
    > {
    > continue;
    > }
    > }
    > --
    > Richard Heathfield <http://www.cpax.org.uk>
    > Email: -http://www. +rjh@
    > "Usenet is a strange place" - dmr 29 July 1999
    > Sig line vacant - apply within


    I am writing some code that needs user input to fill in some structure
    fields in an
    array and I wanted to make sure that it was done safely.

    The scanf below for qty seems to leave a \n that clobbers location. Been
    a while
    since I have done serious coding, I don't remember this many problems
    before :)

    void addpart(PART p[],int *n){

    printf("Adding a part.\n"
    "Please enter a part number:");
    fgets(p[*n].part,sizeof(p[0].part),stdin);
    printf("Please enter part description:");
    fgets(p[*n].description,sizeof(p[0].description),stdin);
    printf("Please enter quantity:");
    scanf("%hu",&p[*n].qty);
    printf("Please enter location:");
    fgets(p[*n].location,5,stdin);
    (*n)++;
    }
     
    sandeep, Jun 2, 2010
    #4
  5. sandeep

    Eric Sosman Guest

    On 6/2/2010 3:08 PM, sandeep wrote:
    > Eric Sosman writes:
    >> On 6/2/2010 2:29 PM, sandeep wrote:
    >>> I wanted to limit how many chars I read in, but the code below instead
    >>> of stopping at
    >>> 7 chars will happily read 30 or 40 car lines and spit them out. Not
    >>> what I expected.
    >>>
    >>> #include<stdio.h>
    >>> main(){
    >>> char line [4096];
    >>>
    >>> while (NULL!=fgets(line,8,stdin))
    >>> {
    >>> printf("%s",line);
    >>> }
    >>>
    >>> return 0;
    >>> }

    >> [...]
    >> --
    >> Eric Sosman
    >> lid


    Please don't quote signatures.

    > I expected that if I typed 123456789 I would get 1234567 out.
    > I threw in a \n in the printf and it now makes some sense.
    >
    > Still not behaving how I hoped having the 89 spill over is not what I
    > wanted, just wanted a nice safe way of inputing size limited fields.


    You need to determine whether fgets() read an entire line,
    or just the beginning of a line with the tail still waiting to
    be read. The determination is easy, or "almost easy:" if the
    buffer contains a newline character, fgets() read an entire line.
    Unfortunately, the opposite is not necessarily true (this is the
    "almost" part): If the buffer has no newline, either fgets()
    filled the buffer and stopped with the tail of the line unread,
    or fgets() encountered end-of-input without seeing a newline (on
    some systems this can't happen; on others it can).

    If all you want to do is discard the unread tail of any too-
    long line, that's easy: When fgets() gives you no newline, just
    read and discard characters until you get a newline or EOF:

    while (NULL != fgets(line, 8, stdin)) {
    if (strchr(line, '\n') == NULL) {
    /* no newline; skip a possible tail */
    int ch;
    while ( (ch = getchar()) != '\n' ) {
    if (ch == EOF) {
    if (ferror(stdin))
    /* respond to I/O error */ ;
    break;
    }
    }
    }
    /* do what you will with the line (or line prefix) */
    }

    --
    Eric Sosman
    lid
     
    Eric Sosman, Jun 2, 2010
    #5
  6. sandeep <> writes:
    > Eric Sosman writes:
    >> On 6/2/2010 2:29 PM, sandeep wrote:
    >>> I wanted to limit how many chars I read in, but the code below instead
    >>> of stopping at
    >>> 7 chars will happily read 30 or 40 car lines and spit them out. Not
    >>> what I expected.
    >>>
    >>> #include<stdio.h>
    >>> main(){
    >>> char line [4096];
    >>>
    >>> while (NULL!=fgets(line,8,stdin))
    >>> {
    >>> printf("%s",line);
    >>> }
    >>>
    >>> return 0;
    >>> }

    >>
    >> Your code reads the first seven characters, prints them,
    >> goes back and reads the next seven, prints them, goes back and reads the
    >> next seven, ... The fgets() function will stop for three reasons: (1)
    >> it reads and stores a '\n', (2) it fills the buffer, or (3) there's no
    >> more input to read (end-of-input or I/O error). In cases (1) and (2),
    >> the next fgets() will resume where the first one left off -- for
    >> example, with the eighth character of a 30-character line.

    >
    > I expected that if I typed 123456789 I would get 1234567 out.


    That's exactly what you got.

    You also got the "89", immediately following the "1234567".

    > I threw in a \n in the printf and it now makes some sense.
    >
    > Still not behaving how I hoped having the 89 spill over is not what I
    > wanted, just wanted a nice safe way of inputing size limited fields.


    What do you mean by "safe"? You can't stop the user from entering
    a long input line. You need to decide how to respond if the user
    does this. One option is to print an error message. Another is
    to silently discard the extra input -- but you still have to read
    it to be able to discard it.

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    Nokia
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
     
    Keith Thompson, Jun 2, 2010
    #6
  7. On Jun 2, 10:08 pm, sandeep <> wrote:
    > just wanted a nice safe way of inputing size limited fields.- Hide quoted text -
    >

    There's no easy weay of doing this, because there is no obvious
    threshold you canuse for sanity testing.

    Humans are unlikely to type more than a few hundred characters at the
    prompt, but free text paragraphs can be very long, and automatically
    generated data can be even longer. fgets() is too difficult to use
    correctly if lines may overflow, because you have to check the
    newline, then do something with the half-read buffer, then recover
    from the error. It's almost as easy to roll your own function.
     
    Malcolm McLean, Jun 3, 2010
    #7
  8. On 3 June, 06:58, Malcolm McLean <>
    wrote:
    > On Jun 2, 10:08 pm, sandeep <> wrote:


    >  just wanted a nice safe way of inputing size limited fields.- Hide quoted text -
    >
    > There's no easy weay of doing this, because there is no obvious
    > threshold you canuse for sanity testing.


    use fgets() to read the characters you want, then call fgetc() or
    fgets() and discard the result until you reach the end of the line.


    > Humans are unlikely to type more than a few hundred characters at the
    > prompt, but free text paragraphs can be very long, and automatically
    > generated data can be even longer. fgets() is too difficult to use
    > correctly if lines may overflow, because you have to check the
    > newline, then do something with the half-read buffer, then recover
    > from the error.


    nonsense. fgets() is perfectly straightforward.


    > It's almost as easy to roll your own function.


    seems a lot of work when the functionality is already provided. Which
    stdio function do you use to implement your roll-your-own?


    --
    The fscanf equivalent of fgets is so simple
    that it can be used inline whenever needed:-
    char s[NN + 1] = "", c;
    int rc = fscanf(fp, "%NN[^\n]%1[\n]", s, &c);
    if (rc == 1) fscanf("%*[^\n]%*c);
    if (rc == 0) getc(fp);
     
    Nick Keighley, Jun 3, 2010
    #8
  9. Nick Keighley <> writes:

    > On 3 June, 06:58, Malcolm McLean <>
    > wrote:

    <snip>
    >> [...] fgets() is too difficult to use
    >> correctly if lines may overflow, because you have to check the
    >> newline, then do something with the half-read buffer, then recover
    >> from the error.

    >
    > nonsense. fgets() is perfectly straightforward.


    I don't agree with Malcolm McLean but I stop some way short of
    "perfectly straightforward". It seems less than straightforward to have
    to scan the array to see if a whole line was read, and the methods that
    avoid this scan are fiddly to get right.

    <snip>
    > --
    > The fscanf equivalent of fgets is so simple
    > that it can be used inline whenever needed:-
    > char s[NN + 1] = "", c;
    > int rc = fscanf(fp, "%NN[^\n]%1[\n]", s, &c);
    > if (rc == 1) fscanf("%*[^\n]%*c);
    > if (rc == 0) getc(fp);


    This is in your sig so you are presumably quoting it for the purposes of
    mockery. The ways in which it falls short of duplicating fgets are
    sufficiently numerous that humour can be the only motivation for
    including it.

    --
    Ben.
     
    Ben Bacarisse, Jun 3, 2010
    #9
  10. On 3 June, 12:31, Richard Heathfield <> wrote:
    > Ben Bacarisse wrote:
    > > Nick Keighley <> writes:


    > >> --
    > >> The fscanf equivalent of fgets is so simple
    > >> that it can be used inline whenever needed:-
    > >>     char s[NN + 1] = "", c;
    > >>     int rc = fscanf(fp, "%NN[^\n]%1[\n]", s, &c);
    > >>     if (rc == 1) fscanf("%*[^\n]%*c);
    > >>     if (rc == 0) getc(fp);

    >
    > > This is in your sig so you are presumably quoting it for the purposes of
    > > mockery.  The ways in which it falls short of duplicating fgets are
    > > sufficiently numerous that humour can be the only motivation for
    > > including it.

    >
    > Um, for one thing it ain't gonna compile.


    I was aware of at least one error in it, but I felt that kind of
    illustrated the point
     
    Nick Keighley, Jun 3, 2010
    #10
    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. Ben Bacarisse

    fgets not doing as I expect.

    Ben Bacarisse, Dec 26, 2008, in forum: C Programming
    Replies:
    8
    Views:
    512
    Andrew McMeikan
    Dec 27, 2008
  2. Stone Free
    Replies:
    5
    Views:
    400
    Stone Free
    Aug 20, 2009
  3. RobGSCL
    Replies:
    3
    Views:
    129
    RobGSCL
    Oct 30, 2006
  4. Simon Strandgaard

    how to expect eof with expect+pty

    Simon Strandgaard, Dec 20, 2006, in forum: Ruby
    Replies:
    4
    Views:
    383
    Simon Strandgaard
    Dec 20, 2006
  5. Phil
    Replies:
    0
    Views:
    178
Loading...

Share This Page