scanf/getchar sequence problem

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • clusardi2k@aol.com

    #1

    scanf/getchar sequence problem

    /*
    The below code on SGI will wait for you to enter 2 things, but
    on Linux it will only wait the first time.

    I can make the code work by replacing the scanf with:

    char data [5];
    fgets (data,5,stdin);
    the_number = atoi (data);

    But, instead I would like to modify the scanf, "/dev/tty", or
    something simple like that.
    */

    #include <stdio.h>
    int main()
    {
    int c;
    int the_number;
    fprintf (stderr,"Enter Number :");
    scanf ("%d",&the_numb er);
    fprintf (stderr,"\nEnte r a letter :");
    c = getchar ();
    if ( (c = getchar ()) == EOF )
    freopen ("/dev/tty","r",stdin) ;
    else
    ungetc (c,stdin);
    return (0);
    }

  • Eric Sosman

    #2
    Re: scanf/getchar sequence problem



    clusardi2k@aol. com wrote:[color=blue]
    > /*
    > The below code on SGI will wait for you to enter 2 things, but
    > on Linux it will only wait the first time.
    >
    > I can make the code work by replacing the scanf with:
    >
    > char data [5];
    > fgets (data,5,stdin);
    > the_number = atoi (data);
    >
    > But, instead I would like to modify the scanf, "/dev/tty", or
    > something simple like that.
    > */
    >
    > #include <stdio.h>
    > int main()
    > {
    > int c;
    > int the_number;
    > fprintf (stderr,"Enter Number :");
    > scanf ("%d",&the_numb er);
    > fprintf (stderr,"\nEnte r a letter :");
    > c = getchar ();
    > if ( (c = getchar ()) == EOF )
    > freopen ("/dev/tty","r",stdin) ;
    > else
    > ungetc (c,stdin);
    > return (0);
    > }[/color]

    Linux is right; SGI is wrong. The scanf() should consume
    any initial white space, an optional sign character, and a
    digit string, but should not consume the following '\n' (or
    whatever else follows the digits). The subsequent getchar()
    should retrieve the '\n' (or whatever).

    My advice is to avoid scanf() for interactive input: it
    can be tricky to use, and recovery from input errors is
    difficult (consider what happens if the user enters "#\n"
    at your first prompt). It is usually better to proceed along
    the lines of the "cure" you discovered: Read the line with
    fgets() and then interpret the characters yourself. It's
    better to use strtod() or sscanf() than atoi(), because the
    latter has essentially *no* way to diagnose erroneous input.

    --
    Eric.Sosman@sun .com


    Comment

    • John Carson

      #3
      Re: scanf/getchar sequence problem

      "Eric Sosman" <eric.sosman@su n.com> wrote in message
      news:d33j3g$l7l $1@news1brm.Cen tral.Sun.COM[color=blue]
      > clusardi2k@aol. com wrote:[color=green]
      >> /*
      >> The below code on SGI will wait for you to enter 2 things, but
      >> on Linux it will only wait the first time.
      >>
      >> I can make the code work by replacing the scanf with:
      >>
      >> char data [5];
      >> fgets (data,5,stdin);
      >> the_number = atoi (data);
      >>
      >> But, instead I would like to modify the scanf, "/dev/tty", or
      >> something simple like that.
      >> */
      >>
      >> #include <stdio.h>
      >> int main()
      >> {
      >> int c;
      >> int the_number;
      >> fprintf (stderr,"Enter Number :");
      >> scanf ("%d",&the_numb er);
      >> fprintf (stderr,"\nEnte r a letter :");
      >> c = getchar ();
      >> if ( (c = getchar ()) == EOF )
      >> freopen ("/dev/tty","r",stdin) ;
      >> else
      >> ungetc (c,stdin);
      >> return (0);
      >> }[/color]
      >
      > Linux is right; SGI is wrong. The scanf() should consume
      > any initial white space, an optional sign character, and a
      > digit string, but should not consume the following '\n' (or
      > whatever else follows the digits). The subsequent getchar()
      > should retrieve the '\n' (or whatever).[/color]

      Yes, but note that there are two getchar() calls in the code, so it should
      be possible to enter a char at the console (though the effect will not be
      the intended one).

      I wonder if the code run on Linux and the code run on SGI were really the
      same.


      --
      John Carson

      Comment

      • Eric Sosman

        #4
        Re: scanf/getchar sequence problem



        John Carson wrote:[color=blue]
        > "Eric Sosman" <eric.sosman@su n.com> wrote
        >
        > [...] The subsequent getchar()[color=green]
        >>should retrieve the '\n' (or whatever).[/color]
        >
        > Yes, but note that there are two getchar() calls in the code, so it should
        > be possible to enter a char at the console (though the effect will not be
        > the intended one).[/color]

        Oh, foozle. I'd like to say "That'll teach me not
        to speed-read code," but experience suggests it won't ...

        --
        Eric.Sosman@sun .com

        Comment

        • S.Tobias

          #5
          Re: scanf/getchar sequence problem

          In comp.lang.c clusardi2k@aol. com wrote:[color=blue]
          > /*
          > The below code on SGI will wait for you to enter 2 things, but
          > on Linux it will only wait the first time.[/color]

          [snip][color=blue]
          > fprintf (stderr,"Enter Number :");
          > scanf ("%d",&the_numb er);
          > fprintf (stderr,"\nEnte r a letter :");
          > c = getchar ();
          > if ( (c = getchar ()) == EOF )[/color]
          [snip]

          On Linux works as expected, ie. waits or not depending
          on what data you feed (at least at an Xterm). Try to
          output values of `c' to see what happens (could it be
          that Enter generates more than one character?)

          --
          Stan Tobias
          mailx `echo siXtY@FamOuS.Be dBuG.pAlS.INVALID | sed s/[[:upper:]]//g`

          Comment

          • clusardi2k@aol.com

            #6
            Re: scanf/getchar sequence problem


            Eric Sosman wrote:[color=blue]
            > clusardi2k@aol. com wrote:[color=green]
            > > /*
            > > The below code on SGI will wait for you to enter 2 things, but
            > > on Linux it will only wait the first time.
            > >
            > > I can make the code work by replacing the scanf with:
            > >
            > > char data [5];
            > > fgets (data,5,stdin);
            > > the_number = atoi (data);
            > >
            > > But, instead I would like to modify the scanf, "/dev/tty", or
            > > something simple like that.
            > > */
            > >
            > > #include <stdio.h>
            > > int main()
            > > {
            > > int c;
            > > int the_number;
            > > fprintf (stderr,"Enter Number :");
            > > scanf ("%d",&the_numb er);
            > > fprintf (stderr,"\nEnte r a letter :");
            > > c = getchar ();
            > > if ( (c = getchar ()) == EOF )
            > > freopen ("/dev/tty","r",stdin) ;
            > > else
            > > ungetc (c,stdin);
            > > return (0);
            > > }[/color]
            >
            > Linux is right; SGI is wrong. The scanf() should consume
            > any initial white space, an optional sign character, and a
            > digit string, but should not consume the following '\n' (or
            > whatever else follows the digits). The subsequent getchar()
            > should retrieve the '\n' (or whatever).[/color]

            This is solution temporarily OK'ed by boss:

            #include <stdio.h>
            int main()
            {
            int c;
            int the_number;
            char car_ret;

            fprintf (stderr,"Enter Number :");

            #ifdef __sgi
            scanf ("%d",&the_numb er);
            #elif defined (__linux)
            scanf ("%d%c",&the_nu mber,&car_ret);
            #endif

            fprintf (stderr,"\nEnte r a letter :");
            c = getchar ();
            if ( (c = getchar ()) == EOF )
            freopen ("/dev/tty","r",stdin) ;
            else
            ungetc (c,stdin);
            return (0);
            }

            Comment

            • Eric Sosman

              #7
              Re: scanf/getchar sequence problem

              clusardi2k@aol. com wrote:[color=blue]
              > [scanf("%d") leaves the newline unconsumed]
              >
              > This is solution temporarily OK'ed by boss:
              >
              > #include <stdio.h>
              > int main()
              > {
              > int c;
              > int the_number;
              > char car_ret;
              >
              > fprintf (stderr,"Enter Number :");
              >
              > #ifdef __sgi
              > scanf ("%d",&the_numb er);
              > #elif defined (__linux)
              > scanf ("%d%c",&the_nu mber,&car_ret);
              > #endif[/color]

              Fragile; a house of cards. If the user enters anything
              at all between the final digit and the newline -- a space
              character, say -- you'll be right back where you started.
              I'll repeat my earlier advice: scanf() is not well suited
              for interactive input, and you'd be better off using another
              method altogether.

              --
              Eric Sosman
              esosman@acm-dot-org.invalid

              Comment

              • pete

                #8
                Re: scanf/getchar sequence problem

                Eric Sosman wrote:[color=blue]
                >
                > clusardi2k@aol. com wrote:[color=green]
                > > [scanf("%d") leaves the newline unconsumed]
                > >
                > > This is solution temporarily OK'ed by boss:
                > >
                > > #include <stdio.h>
                > > int main()
                > > {
                > > int c;
                > > int the_number;
                > > char car_ret;
                > >
                > > fprintf (stderr,"Enter Number :");
                > >
                > > #ifdef __sgi
                > > scanf ("%d",&the_numb er);
                > > #elif defined (__linux)
                > > scanf ("%d%c",&the_nu mber,&car_ret);
                > > #endif[/color]
                >
                > Fragile; a house of cards. If the user enters anything
                > at all between the final digit and the newline -- a space
                > character, say -- you'll be right back where you started.
                > I'll repeat my earlier advice: scanf() is not well suited
                > for interactive input, and you'd be better off using another
                > method altogether.[/color]

                Is stderr well suited for interactive output?
                I always thought that the purpose of the standard error stream
                was to output error messages.

                --
                pete

                Comment

                • Karl Heinz Buchegger

                  #9
                  Re: scanf/getchar sequence problem

                  Eric Sosman wrote:[color=blue]
                  >
                  > I'll repeat my earlier advice: scanf() is not well suited
                  > for interactive input, and you'd be better off using another
                  > method altogether.[/color]

                  And it is a very good advice. The OP should follow it.
                  The substitution of scanf with a fgets() sscanf() pair isn't
                  much work and saves you from that problem (and others
                  you have not discovered yet).

                  char Buffer[ BUFFER_SIZE ]
                  fgets( Buffer, sizeof( Buffer ), stdin );
                  sscanf( "... whatever you want

                  The only problem is the BUFFER_SIZE. It doesn't matter which number
                  you choose, there is always the possibility that some user might overflow
                  it. If you set it large enough, say 4096, that probability is very low (although
                  not 0.0). In such cases it is often sufficient to just detect that fact (which
                  can easily be done: just look for a '\n' character in Buffer) and ask the user
                  for less input.

                  --
                  Karl Heinz Buchegger
                  kbuchegg@gascad .at

                  Comment

                  • CBFalconer

                    #10
                    Re: scanf/getchar sequence problem

                    Eric Sosman wrote:[color=blue]
                    > clusardi2k@aol. com wrote:
                    >[color=green]
                    >> [scanf("%d") leaves the newline unconsumed]
                    >>
                    >> This is solution temporarily OK'ed by boss:
                    >>
                    >> #include <stdio.h>
                    >> int main()
                    >> {
                    >> int c;
                    >> int the_number;
                    >> char car_ret;
                    >>
                    >> fprintf (stderr,"Enter Number :");
                    >>
                    >> #ifdef __sgi
                    >> scanf ("%d",&the_numb er);
                    >> #elif defined (__linux)
                    >> scanf ("%d%c",&the_nu mber,&car_ret);
                    >> #endif[/color]
                    >
                    > Fragile; a house of cards. If the user enters anything
                    > at all between the final digit and the newline -- a space
                    > character, say -- you'll be right back where you started.
                    > I'll repeat my earlier advice: scanf() is not well suited
                    > for interactive input, and you'd be better off using another
                    > method altogether.[/color]

                    Actually, used properly scanf is quite suitable, as long as you are
                    willing to have the overhead of that large interpreter.

                    /* get a numeric and flush the input line */
                    /* return 0 for success, non-zero otherwise */
                    int getnum(int *num)
                    {
                    int ok, ch;

                    ok = scanf("%d", num);
                    while (EOF != (ch = getchar()) && ('\n' != ch)) continue;
                    return (1 == ok);
                    }

                    although I would never use it in that form. I keep a separate
                    flushln(f) function about, and would simply write:

                    if (1 == scanf("%d", &num)) .....
                    if (EOF == flushln(stdin)) .....

                    as long as people realize that leading blanks and empty lines will
                    be skipped. The key to suitable use of scanf interactively is the
                    "if (1 == scanf(....". Anything other than one leads to confusion,
                    gnashing of teeth, and loss of hair. The advantage is the
                    elimination of presized buffers.

                    --
                    "If you want to post a followup via groups.google.c om, don't use
                    the broken "Reply" link at the bottom of the article. Click on
                    "show options" at the top of the article, then click on the
                    "Reply" at the bottom of the article headers." - Keith Thompson


                    Comment

                    • Richard Bos

                      #11
                      Re: stderr, Re: scanf/getchar sequence problem

                      Richard Herring <junk@[127.0.0.1]> wrote:
                      [color=blue][color=green][color=darkred]
                      > >> pete <pfiland@mindsp ring.com>
                      > >>> #include <stdio.h>
                      > >>> int main()
                      > >>> {
                      > >>> int c;
                      > >>> int the_number;
                      > >>> fprintf (stderr,"Enter Number :");
                      > >>> scanf ("%d",&the_numb er);
                      > >>> fprintf (stderr,"\nEnte r a letter :");[/color][/color]
                      >
                      > Oh, I see. Well, the answer would be "yes", though personally, since
                      > this is crossposted to clc++ I'd prefer cerr.
                      >
                      > The user may have redirected stdout to a file or piped it to another
                      > program, so they wouldn't see prompts directed to it. And stderr is
                      > automatically flushed, so if you're lucky the prompts will appear before
                      > the input operation starts.[/color]

                      If, yes. If you're unlucky, though, stderr will appear in the console
                      window, not in the user's main window, and he'll completely overlook it.
                      The sysadmin, OTOH, will be pissed off at you for spamming his log files
                      with completely meaningless messages. stderr is for error messages, not
                      for prompts. Besides, if stdout _is_ redirected, that was intentional.

                      Richard

                      Comment

                      • Richard Herring

                        #12
                        Re: stderr, Re: scanf/getchar sequence problem

                        In message <4265179c.15503 077@news.xs4all .nl>, Richard Bos
                        <rlb@hoekstra-uitgeverij.nl> writes[color=blue]
                        >Richard Herring <junk@[127.0.0.1]> wrote:
                        >[color=green][color=darkred]
                        >> >> pete <pfiland@mindsp ring.com>
                        >> >>> #include <stdio.h>
                        >> >>> int main()
                        >> >>> {
                        >> >>> int c;
                        >> >>> int the_number;
                        >> >>> fprintf (stderr,"Enter Number :");
                        >> >>> scanf ("%d",&the_numb er);
                        >> >>> fprintf (stderr,"\nEnte r a letter :");[/color]
                        >>
                        >> Oh, I see. Well, the answer would be "yes", though personally, since
                        >> this is crossposted to clc++ I'd prefer cerr.
                        >>
                        >> The user may have redirected stdout to a file or piped it to another
                        >> program, so they wouldn't see prompts directed to it. And stderr is
                        >> automatically flushed, so if you're lucky the prompts will appear before
                        >> the input operation starts.[/color]
                        >
                        >If, yes. If you're unlucky, though, stderr will appear in the console
                        >window,[/color]

                        What is a "console window"?
                        [color=blue]
                        > not in the user's main window, and he'll completely overlook it.[/color]

                        Just as he would if he redirected stdout to a file, or piped it to
                        another program.
                        [color=blue]
                        >The sysadmin,[/color]

                        What is a "sysadmin"?
                        [color=blue]
                        >OTOH, will be pissed off at you for spamming his log files
                        >with completely meaningless messages.[/color]

                        Even back in the days when I had to manage a mainframe system, I don't
                        recall messages to stderr from user programs ever going anywhere but the
                        user's teletype. But yes, it's true that the way the standard streams
                        are connected to the hardware is beyond the scope of the C or C++
                        standards, so anything could happen.
                        [color=blue]
                        >stderr is for error messages, not
                        >for prompts.[/color]

                        And stdout is for computed output, not for prompts. In the absence of a
                        stdprompt, one has to use one or the other, and the decision is not best
                        made on the basis of its name but on how it interacts with the outside
                        world.
                        [color=blue]
                        >Besides, if stdout _is_ redirected, that was intentional.[/color]

                        Of course. It's not something one does by accident. But intentions
                        differ.

                        --
                        Richard Herring

                        Comment

                        • Richard Bos

                          #13
                          Re: stderr, Re: scanf/getchar sequence problem

                          Richard Herring <junk@[127.0.0.1]> wrote:
                          [color=blue]
                          > In message <4265179c.15503 077@news.xs4all .nl>, Richard Bos
                          > <rlb@hoekstra-uitgeverij.nl> writes[color=green]
                          > >The sysadmin,[/color]
                          >
                          > What is a "sysadmin"?[/color]

                          Someone you are obviously in dire need of.

                          HTH; HAND.

                          Richard

                          Comment

                          • Richard Herring

                            #14
                            Re: stderr, Re: scanf/getchar sequence problem

                            In message <426523bb.18605 919@news.xs4all .nl>, Richard Bos
                            <rlb@hoekstra-uitgeverij.nl> writes[color=blue]
                            >Richard Herring <junk@[127.0.0.1]> wrote:
                            >[color=green]
                            >> In message <4265179c.15503 077@news.xs4all .nl>, Richard Bos
                            >> <rlb@hoekstra-uitgeverij.nl> writes[color=darkred]
                            >> >The sysadmin,[/color]
                            >>
                            >> What is a "sysadmin"?[/color]
                            >
                            >Someone you are obviously in dire need of.[/color]

                            What was your username again?

                            --
                            Richard "clickety click" Herring

                            Comment

                            Working...