Re: scanf dilemma for beginner

Discussion in 'C Programming' started by chris, Aug 29, 2003.

  1. chris

    chris Guest

    Thanks to everyone who replied. I received great info, which will be
    attempted and contemplated soon. I appreciate everyone's time.

    I've realized something though, so I have another question. And it's
    related to Steve Zimmerman's code regarding the space in scanf. I have
    discovered that using the following call to scanf works fine (from what I
    can tell - there may be something going on in the background I'm not aware
    of however):

    scanf(" %c", &ch)

    ---> nothing else needs to be changed, see code below,
    with question following

    > #include <stdio.h>
    >
    >
    >
    > int main(void)
    > {
    >
    > int num = 0;
    > char ch = 0;
    > int i;
    >
    > printf("Enter decimal number (or zero for list): ");
    > scanf("%d",&num);
    > if (num == 0) {
    > for (i=1; i <= 100; i++)
    > printf("\nDecimal: %3d\tOctal: %3o\tHex: %3x",i,i,i);
    > return 1;
    > }
    >
    > printf("\nWould you like (h)ex or (o)ctal? ");
    > scanf("%c",&ch);

    ^^^^
    if I use scanf(" %c", &ch) - the space works great.


    > if (ch == 'h' || ch == 'H')
    > printf("\n\n%d in Hexadecimal is: %x", num, num);
    > else if (ch == 'o' || ch == 'O')
    > printf("\n\n%d in Octal is: %o",num,num);
    > else {
    > printf("\nSorry, but there was some sort of error. Cheers!\n\n");
    > return 2;
    > }
    > return 0;
    > }
    >



    So, my question is - what does the space do? Does it make scanf read the
    next character in the buffer but just ignore it? Inquiring minds want to
    know. :)


    Thanks again for the help!
     
    chris, Aug 29, 2003
    #1
    1. Advertising

  2. chris wrote:

    > Thanks to everyone who replied. I received great info, which will be
    > attempted and contemplated soon. I appreciate everyone's time.
    >
    > I've realized something though, so I have another question. And it's
    > related to Steve Zimmerman's code regarding the space in scanf. I have
    > discovered that using the following call to scanf works fine (from what I
    > can tell - there may be something going on in the background I'm not aware
    > of however):
    >
    > scanf(" %c", &ch)
    >
    > ---> nothing else needs to be changed, see code below,
    > with question following
    >
    >
    >>#include <stdio.h>
    >>
    >>
    >>
    >>int main(void)
    >>{
    >>
    >> int num = 0;
    >> char ch = 0;
    >> int i;
    >>
    >> printf("Enter decimal number (or zero for list): ");
    >> scanf("%d",&num);
    >> if (num == 0) {
    >> for (i=1; i <= 100; i++)
    >> printf("\nDecimal: %3d\tOctal: %3o\tHex: %3x",i,i,i);
    >> return 1;
    >> }
    >>
    >> printf("\nWould you like (h)ex or (o)ctal? ");
    >> scanf("%c",&ch);
    >>

    > ^^^^
    > if I use scanf(" %c", &ch) - the space works great.
    >
    >
    >
    >> if (ch == 'h' || ch == 'H')
    >> printf("\n\n%d in Hexadecimal is: %x", num, num);
    >> else if (ch == 'o' || ch == 'O')
    >> printf("\n\n%d in Octal is: %o",num,num);
    >> else {
    >> printf("\nSorry, but there was some sort of error. Cheers!\n\n");
    >> return 2;
    >> }
    >> return 0;
    >>}
    >>
    >>

    >
    >
    > So, my question is - what does the space do? Does it make scanf read the
    > next character in the buffer but just ignore it? Inquiring minds want to
    > know. :)
    >
    >
    > Thanks again for the help!




    Thank you, Chris, for this excellent question.

    Without the space before the percentage sign, the following statement

    scanf("%c", &ch);

    assigns the newline character that you pressed in response to the statement:

    printf("Enter decimal number (or zero for list): ");

    to the variable ch. Then, since ch != H, h, O, or o, you get the
    error statement.

    The reason the program works with the space in the scanf statement,
    is because the scanf statement interprets the space as saying,
    "Ignore all whitespace characters." Hence, the newline
    character (which is a whitespace character) is ignored.

    Perhaps a reason that scanf is not a universally popular function
    is precisely because of these space/whitespace peculiarities.
    I personally like scanf, and consider the peculiarities worth getting
    used to.

    Regards,

    Steve
     
    Steve Zimmerman, Aug 30, 2003
    #2
    1. Advertising

  3. On Fri, 29 Aug 2003 22:14:51 UTC, chris <> wrote:

    > Thanks to everyone who replied. I received great info, which will be
    > attempted and contemplated soon. I appreciate everyone's time.
    >
    > I've realized something though, so I have another question. And it's
    > related to Steve Zimmerman's code regarding the space in scanf. I have
    > discovered that using the following call to scanf works fine (from what I
    > can tell - there may be something going on in the background I'm not aware
    > of however):
    >

    scanf() is defined to read from a source that is well defined. That is
    if you tries to read a number and the user puts something else your
    scanf() fails.

    In long years of programming in C I found that nothing than getc() (or
    fgetc() or (f)getchar()) comes on the user friendlyness a good program
    needs to scan user (or other unsecure) input.

    Use getc() to get any char that comes along in the stream. Test it to
    see if it matches your exeptation.

    Hint: isnum() check a char for numeric
    hint: isalpha() check a char for not numeric but a letter
    hint: there are more is... functions in the C library, play with them
    hint: it is guaranteed that any number you can get is '0' '1' ....'9'
    in sequence
    hint: it is NOT guaranteed that 'A' through 'Z' or 'a' through 'z' is
    a sequence
    hint: '9' - '0' converts a digit (here the '9') from the external
    charset to binary
    play around a bit with converting numbers from input to
    binary (eg. signed or unsigned int)
    it's easy to do with the hints above. Think on your first
    year of scool as you learned to
    add, subtract, multiply numbers like 12 or 234. A string
    coming in looks like '1' '2' but you needs
    the 12 in binary
    hint: don't use scanf() to read strings as scanf() is designed bad!
    You've no control how many chars the function will read. This can
    occure buffer overflow. fscanf() is better for that - but getc() gives
    you more control again.

    --
    Tschau/Bye
    Herbert

    eComStation 1.1 Deutsch Beta ist verügbar
     
    The Real OS/2 Guy, Aug 31, 2003
    #3
  4. On Sun, 31 Aug 2003 06:08:21 UTC, Richard Heathfield
    <> wrote:

    > The Real OS/2 Guy wrote:
    >
    > > In long years of programming in C I found that nothing than getc() (or
    > > fgetc() or (f)getchar()) comes on the user friendlyness a good program
    > > needs to scan user (or other unsecure) input.

    >
    > There is no fgetchar function.
    >
    > > Use getc() to get any char that comes along in the stream. Test it to
    > > see if it matches your exeptation.
    > >
    > > Hint: isnum() check a char for numeric

    >
    > C has no isnum() function. You are thinking of isdigit(), I hope.


    You're Right.

    > <snip>
    >
    > > hint: don't use scanf() to read strings

    >
    > That's good advice in my opinion.
    >
    > > as scanf() is designed bad!

    >
    > I don't know if I'd call it "bad". It's certainly not as intuitive as I'd
    > like it to be.
    >
    > > You've no control how many chars the function will read.

    >
    > You have, *if* you know how to use that control.


    Really?
    Try to read

    "a quoted long quoted text quote goes on and on and on and on!",4711-,
    3e-4,"qute"
    "a short q. txt",32,4-,"much more text\rand more, many, many ore"
    unquoted but legal here,99-,-12.34.12345678,no quote. legal

    with a single scanf. Read each text, filter the colons, read the
    numbers as int when neither exponent nor decimal point is given, else
    as double. Note the trailing sign!

    How does you control that a string gets readed correctly without
    buffer overflow when you assumes that the string has only 25 char max
    but comes along with 345 chars in it? You've really NO control when
    the input stream differss from thast you awaits.

    > > This can
    > > occure buffer overflow. fscanf() is better for that -

    >
    > No, it isn't. It's precisely the same issue. scanf( is merely sugar for
    > fscanf(stdin,
    >
    > > but getc() gives
    > > you more control again.

    >
    > My own preference is to use a custom routine to read an entire line, and
    > then parse it at my leisure.
    >

    How long should the line be? You awaits legally 25 chars - but the
    user gives you 2047. How does you handle this?
    How reads you a string:

    string 1 string1end, s t r i n g 2 s t r i
    n g 2.

    in 2 variables using scanf(), the white spaces are members of the
    string!

    It is more complicated to read something in a buffer to start to read
    the buffer to copy the buffer! Use getc() to get char by char, build
    the real destination by interpreting what you get, break the input
    when you sees an error. flush the input when you gets out of sync (you
    knows you can't use fflush() for that!

    Convert incoming numeric values on the fly (you can easyly test the
    format, you can accept formats the standard libray can't, like
    123,456.58- or 14.3.2004 as date or 23.14 as time or 1.234.567,89 EUR
    as formatted (european) amount of money.

    When you've mad that multiple times you'll have douzends of this
    little helpers in you tray and been extremely more flexible than
    fgets(), fscanf() and so on ever can be.

    Whenever you have a trusted format you can use all the library
    functions. But whenever you can't trust the input all of them (except
    (f)getc() or (f)getchar()) because the are not designed to handle
    untrusted streams.

    Whenever you must touch each char it makes no sense to fill a buffer
    and then touch all chars again and again and....
    - You'll ever have the problem that you buffer is too small to hold a
    complete line.
    - you've overhead in allocation more and more buffer only to get a
    whole line
    because you don't know how big the line will be until you've readed
    it completely

    Your OS holds a buffer to buffer a line, your C runtime holds a buffer
    to buffer the line too, youself will hold another buffer to hold the
    line only to look at each char to handle it? You needs some special
    actions when the buffer is shorter as the line incoming.

    You saves both, a dynamic buffer to store temprary and the time it
    costs to compy the buffer (runtime) to your buffer, the work to handle
    possible buffer overflow, longer lines and so on.

    with getc() you saves:
    - a buffer for the buffer
    - lots of copy actions
    - risk of bufferoverflow

    and you wins
    - exact control over each char incoming

    --
    Tschau/Bye
    Herbert

    eComStation 1.1 Deutsch Beta ist verügbar
     
    The Real OS/2 Guy, Aug 31, 2003
    #4
  5. Richard Heathfield <> wrote in
    <bis3ck$hfm$>:

    <BIG SNIP>
    >
    >My own preference is to use a custom routine to read an entire line, and
    >then parse it at my leisure.


    Same to me. If you want to make input foolproof you have to keep
    in mind how creative fools can be. :)

    <OT> To R.H.:
    Yes, it's me, I'm back, and I didn't include the word
    charter ... oh, shoot! :)
    </OT>

    --
    My opinions are not those of my ex-employer.
     
    Irrwahn Grausewitz, Aug 31, 2003
    #5
  6. The Real OS/2 Guy wrote:
    > On Sun, 31 Aug 2003 06:08:21 UTC, Richard Heathfield wrote:
    >> The Real OS/2 Guy wrote:

    <snip>
    >>
    >> > hint: don't use scanf() to read strings

    >>
    >> That's good advice in my opinion.
    >>
    >> > as scanf() is designed bad!

    >>
    >> I don't know if I'd call it "bad". It's certainly not as intuitive as I'd
    >> like it to be.
    >>
    >> > You've no control how many chars the function will read.

    >>
    >> You have, *if* you know how to use that control.

    >
    > Really?
    > Try to read
    >
    > "a quoted long quoted text quote goes on and on and on and on!",4711-,
    > 3e-4,"qute"
    > "a short q. txt",32,4-,"much more text\rand more, many, many ore"
    > unquoted but legal here,99-,-12.34.12345678,no quote. legal
    >
    > with a single scanf.


    No, thanks. I make a special point of avoiding scanf at all costs. But scanf
    can certainly do it. Ask Dan Pop if you want to know how.


    > Read each text, filter the colons, read the
    > numbers as int when neither exponent nor decimal point is given, else
    > as double. Note the trailing sign!


    Why not just read it as a string?

    > How does you control that a string gets readed correctly without
    > buffer overflow when you assumes that the string has only 25 char max
    > but comes along with 345 chars in it? You've really NO control when
    > the input stream differss from thast you awaits.


    So don't make that assumption. That makes it harder to use scanf properly,
    which is a good reason to avoid it, as if we needed any more reasons.

    <snip>

    >> > but getc() gives
    >> > you more control again.

    >>
    >> My own preference is to use a custom routine to read an entire line, and
    >> then parse it at my leisure.
    >>

    > How long should the line be? You awaits legally 25 chars - but the
    > user gives you 2047. How does you handle this?


    By reallocating as I go, instead of "awaiting legally 25 chars". See for
    yourself:

    http://users.powernet.co.uk/eton/c/fgetline.h
    http://users.powernet.co.uk/eton/c/fgetline.c


    > How reads you a string:
    >
    > string 1 string1end, s t r i n g 2 s t r i
    > n g 2.


    Easy.

    char *line = NULL;
    size_t size = 0;
    if(0 == fgetline(&line, &size, (size_t)-1, fp, 0))
    {
    I have the whole line in memory
    }

    >
    > in 2 variables using scanf(), the white spaces are members of the
    > string!


    This, too, can be done by scanf aficionados.

    > It is more complicated to read something in a buffer to start to read
    > the buffer to copy the buffer!


    Parse error.

    <snip>

    > Whenever you must touch each char it makes no sense to fill a buffer
    > and then touch all chars again and again and....


    Depends on what you're doing. If I'm doing a character-based filter (e.g. a
    stream cipher or a case converter), clearly I'm not going to bother reading
    a buffer.

    > - You'll ever have the problem that you buffer is too small to hold a
    > complete line.


    Nope.

    > - you've overhead in allocation more and more buffer only to get a
    > whole line


    Nope. Just as much as is necessary.

    --
    Richard Heathfield :
    "Usenet is a strange place." - Dennis M Ritchie, 29 July 1999.
    C FAQ: http://www.eskimo.com/~scs/C-faq/top.html
    K&R answers, C books, etc: http://users.powernet.co.uk/eton
     
    Richard Heathfield, Aug 31, 2003
    #6
  7. "The Real OS/2 Guy" <> wrote in
    <wmzsGguTDN6N-pn2-5UqfXtM97rmP@moon>:

    <SNIP>
    >>
    >> My own preference is to use a custom routine to read an entire line, and
    >> then parse it at my leisure.
    >>

    >How long should the line be? You awaits legally 25 chars - but the
    >user gives you 2047. How does you handle this?
    >

    Dynamically resize the buffer while reading input char-by-char.
    We had this elsethread recently.

    >
    >It is more complicated to read something in a buffer to start to read
    >the buffer to copy the buffer! Use getc() to get char by char, build
    >the real destination by interpreting what you get, break the input
    >when you sees an error. flush the input when you gets out of sync (you
    >knows you can't use fflush() for that!


    Sometimes it's good to have more than one character lookahead
    when parsing input. In some cases it's ok to not throw away
    remaining input and re-sync in order to provide some sort of
    error recovery.

    >
    >Whenever you have a trusted format you can use all the library
    >functions. But whenever you can't trust the input all of them (except
    >(f)getc() or (f)getchar()) because the are not designed to handle
    >untrusted streams.

    You *can* trust fgets().
    And there's still no fgetchar() function :)

    >Whenever you must touch each char it makes no sense to fill a buffer
    >and then touch all chars again and again and....

    Ever tried to ungetc() more than one character to stdin???

    >- You'll ever have the problem that you buffer is too small to hold a
    >complete line.

    realloc() your buffer to a suitable size

    >- you've overhead in allocation more and more buffer only to get a
    >whole line
    > because you don't know how big the line will be until you've readed
    >it completely

    Theoretically correct. Practically exorbitantly high line lengths
    are not very likely to occur /that/ often.

    >
    >Your OS holds a buffer to buffer a line, your C runtime holds a buffer
    >to buffer the line too, youself will hold another buffer to hold the
    >line only to look at each char to handle it?

    Yes. Because to my own buffer I can do whatever I like, even
    things I cannot do to my implementation's input buffer.

    > You needs some special
    >actions when the buffer is shorter as the line incoming.

    Yup. realloc().

    >You saves both, a dynamic buffer to store temprary and the time it
    >costs to compy the buffer (runtime) to your buffer, the work to handle
    >possible buffer overflow, longer lines and so on.

    Personally, I'm fine with this overhead, as long as I gain
    more flexibility.

    >with getc() you saves:
    >- a buffer for the buffer
    >- lots of copy actions
    >- risk of bufferoverflow

    No risk when using fgets() (correctly, that is).
    Definitely no risk when using fgetc() in your own
    implementation of a gets-routine which uses
    dynamically allocated buffer.

    >
    >and you wins
    >- exact control over each char incoming

    And loose control on ambigous input where you need more than one
    character overhead (I know, these cases are rare, but...).

    After all, it's merely a question of personal preferences, which
    approach to use to the given problem of input parsing. So, it's
    not a big deal to disagree on it.


    --
    If you can see it and it’s there - it’s real.
    If you can’t see it, but it’s there - it’s transparent.
    If you can see it but it isn’t there - it’s virtual.
    If you can’t see it and it isn’t there - it’s gone.
     
    Irrwahn Grausewitz, Aug 31, 2003
    #7
  8. Irrwahn Grausewitz <> wrote in
    <>:

    >character overhead (I know, these cases are rare, but...).

    ^^^^^^^^
    Should read: lookahead


    --
    Learn how to splel, dmanit!
     
    Irrwahn Grausewitz, Aug 31, 2003
    #8
  9. chris

    LibraryUser Guest

    The Real OS/2 Guy wrote:
    > Richard Heathfield <> wrote:
    >

    .... snip ...
    > >
    > > My own preference is to use a custom routine to read an entire
    > > line, and then parse it at my leisure.
    > >

    > How long should the line be? You awaits legally 25 chars - but the
    > user gives you 2047. How does you handle this?
    > How reads you a string:
    >
    > string 1 string1end, s t r i n g 2 s t r i
    > n g 2.
    >
    > in 2 variables using scanf(), the white spaces are members of the
    > string!


    I'm sure Richard has a slightly different answer, but I use
    ggets, available at:

    <http://cbfalconer.home.att.net/download/>

    which adapts itself to the incoming line length on the fly, using
    the magic of realloc. It combines the simplicity of gets with
    the safety of fgets.

    --
    Replies should be to the newsgroup
    Chuck Falconer, on vacation.
     
    LibraryUser, Aug 31, 2003
    #9
  10. "The Real OS/2 Guy" <> wrote in
    <wmzsGguTDN6N-pn2-nLfghd2yuL32@moon>:

    <BIG SNIP>

    I agree with most of what you've said in your reply, but
    the OP's problem was with interactive user input, not
    stream filters, so there are no performance issues - the
    user is the bottleneck here, not buffer manipulation. And
    it's somewhat unlikely that someone will enter data
    manually in a manner that realloc() will fail.

    Irrwahn

    --
    If it's not on fire, it's a software problem.
     
    Irrwahn Grausewitz, Aug 31, 2003
    #10
  11. On Sun, 31 Aug 2003 19:20:49 UTC, Irrwahn Grausewitz
    <> wrote:

    > "The Real OS/2 Guy" <> wrote in
    > <wmzsGguTDN6N-pn2-nLfghd2yuL32@moon>:
    >
    > <BIG SNIP>
    >
    > I agree with most of what you've said in your reply, but
    > the OP's problem was with interactive user input, not
    > stream filters,


    Even that is a stream filter. The source is stdin, the destination
    stdout. Typical filter:
    - read something from stdin, write something to stdout, write errors
    to stderr
    C knows nothing about devices, so the keyboard comes as stdin and the
    typewriter/high res screen as stdout.
    C knows nothing about interactive I/O. But it knows perfectly of
    streams.

    so there are no performance issues - the
    > user is the bottleneck here, not buffer manipulation. And
    > it's somewhat unlikely that someone will enter data
    > manually in a manner that realloc() will fail.


    realloc can only reserve memory it has. realloc fails quickly when the
    system is out of (virtual) memory.

    Time is always an issue, even when the user types slowly - the work
    the application has to do on his/her input is always a boddlenec
    because the user awaits the result of his input immediately. And even
    when you'd designed a program for interactive usage it can be run in
    batch mode whereas the input is a 2 GB file redirected to stdout
    (cat file | app 2>app.log -c app.config | tee file2)

    --
    Tschau/Bye
    Herbert

    eComStation 1.1 Deutsch Beta ist verügbar
     
    The Real OS/2 Guy, Sep 1, 2003
    #11
  12. On Mon, 1 Sep 2003 13:02:07 UTC, LibraryUser
    <> wrote:

    > The Real OS/2 Guy wrote:
    > > <> wrote:
    > >

    > ... snip ...
    > > >
    > > > Sometimes it's good to have more than one character lookahead
    > > > when parsing input. In some cases it's ok to not throw away
    > > > remaining input and re-sync in order to provide some sort of
    > > > error recovery.

    > >
    > > Correct -but for that it's quite easy to write a wrapper around
    > > getc and ungetc to exted the ability of ungetc to put more chars
    > > back to stream.

    >
    > Except that, having done that, most of the standard library (and
    > maybe much more) does not use those wrappers, leading to
    > confusion. You find you have ungetted several characters, and
    > then call, say, scanf(), which takes an entirely different view
    > of the world, and leaves unconnected things in your pseudo input
    > stream.
    >
    > So, if you do such things, for sanity you need to replace
    > anything and everything to do with char i/o.


    That it's. You use getc() and nothing else to read from that stream.
    Ever you gets up mixing differen function in input you'll end up with
    an uncontrolled stream! Being consistent is the maxim.

    There is nothing that hinders you to use fscans() on other, trusted
    streams where reading char by char on the untrusted stream.

    --
    Tschau/Bye
    Herbert

    eComStation 1.1 Deutsch Beta ist verügbar
     
    The Real OS/2 Guy, Sep 1, 2003
    #12
    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. Chris
    Replies:
    0
    Views:
    412
    Chris
    Nov 10, 2003
  2. Ian Roddis

    Re: scanf dilemma for beginner

    Ian Roddis, Aug 29, 2003, in forum: C Programming
    Replies:
    0
    Views:
    439
    Ian Roddis
    Aug 29, 2003
  3. Greg P.

    Re: scanf dilemma for beginner

    Greg P., Aug 29, 2003, in forum: C Programming
    Replies:
    7
    Views:
    384
    Kevin Easton
    Aug 30, 2003
  4. =?ISO-8859-1?Q?Martin_J=F8rgensen?=

    scanf (yes/no) - doesn't work + deprecation errors scanf, fopen etc.

    =?ISO-8859-1?Q?Martin_J=F8rgensen?=, Feb 16, 2006, in forum: C Programming
    Replies:
    185
    Views:
    3,449
    those who know me have no need of my name
    Apr 3, 2006
  5. =?ISO-8859-1?Q?Martin_J=F8rgensen?=

    difference between scanf("%i") and scanf("%d") ??? perhaps bug inVS2005?

    =?ISO-8859-1?Q?Martin_J=F8rgensen?=, Apr 26, 2006, in forum: C Programming
    Replies:
    18
    Views:
    689
    Richard Bos
    May 2, 2006
Loading...

Share This Page