Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C Programming > scanf behaviour

Reply
Thread Tools

scanf behaviour

 
 
stasgold@gmail.com
Guest
Posts: n/a
 
      11-24-2006
Hello.

I maybe reinvent the weel ...
I'm trying to read positive integer number with the help of scanf, if
the input value is not positive number but negaive one zero or char , i
have to reread the input until I get the needed pos. number

I wrote the code , but it doesn't work the way it should : if i put
some char into input, the program goes infinite loop instead of
promting me to enter a new value.

Please shed some light on what is the problem. thanks
Code:
#include <stdio.h>

int main()
{
	int n;
	while (1)
	{
		printf("Please enter positive number \n");
		if ( scanf("%d",&n)!=1 || n<=0 )
		{
			printf("Illegal input!\n");
			continue; 
		}
		
	return 0;
	}
return 1;
}
 
Reply With Quote
 
 
 
 
Ian Collins
Guest
Posts: n/a
 
      11-24-2006
http://www.velocityreviews.com/forums/(E-Mail Removed) wrote:
> Hello.
>
> I maybe reinvent the weel ...
> I'm trying to read positive integer number with the help of scanf, if
> the input value is not positive number but negaive one zero or char , i
> have to reread the input until I get the needed pos. number
>
> I wrote the code , but it doesn't work the way it should : if i put
> some char into input, the program goes infinite loop instead of
> promting me to enter a new value.
>

That's because the conversion fails and the characters remain in the
stream. So the next time scanf is called, the same character sequence is
read.

Add

fflush( stdin );

after your printf.

> Please shed some light on what is the problem. thanks
>
Code:
> #include <stdio.h>
> 
> int main()
> {
> 	int n;
> 	while (1)
> 	{
> 		printf("Please enter positive number \n");
> 		if ( scanf("%d",&n)!=1 || n<=0 )
> 		{
> 			printf("Illegal input!\n");
> 			continue; 
> 		}
> 		
> 	return 0;
> 	}
> return 1;
> }
>
>



--
Ian Collins.
 
Reply With Quote
 
 
 
 
Richard Heathfield
Guest
Posts: n/a
 
      11-24-2006
Ian Collins said:

> Add
>
> fflush( stdin );


No, don't do that. The behaviour of fflush on streams open for input is
undefined.

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at the above domain, - www.
 
Reply With Quote
 
Ian Collins
Guest
Posts: n/a
 
      11-24-2006
Richard Heathfield wrote:
> Ian Collins said:
>
>
>>Add
>>
>>fflush( stdin );

>
>
> No, don't do that. The behaviour of fflush on streams open for input is
> undefined.
>

Oops, so it is.

I've always used the Solaris man page definition:

"If stream points to an input stream or an update stream into
which the most recent operation was input, that stream is
flushed if it is seekable and is not already at end-of-file."

--
Ian Collins.
 
Reply With Quote
 
Jens Thoms Toerring
Guest
Posts: n/a
 
      11-25-2006
(E-Mail Removed) wrote:
> I maybe reinvent the weel ...


No, you just found out that scanf() is not very well suited for
dealing with user input. You're definitely not the first and
won't be the last

> I'm trying to read positive integer number with the help of scanf, if
> the input value is not positive number but negaive one zero or char , i
> have to reread the input until I get the needed pos. number


> I wrote the code , but it doesn't work the way it should : if i put
> some char into input, the program goes infinite loop instead of
> promting me to enter a new value.


> Please shed some light on what is the problem. thanks


> #include <stdio.h>


> int main()


int main( void )

tends to be better since your main() function doesn't take any input.

> {
> int n;
> while (1)
> {
> printf("Please enter positive number \n");
> if ( scanf("%d",&n)!=1 || n<=0 )


The problem you're facing is that when scanf() doesn't find what
it's looking for it won't read anything at all. So if the user inputs
something that doesn't start with a number (with optionally a '+' or
'-' in front of it) it stops immediately and nothing of the users
input gets consumed. When scanf() then tries to read again it still
finds the same characters it couldn't deal with already the last time
and bails out again. Repeat at nauseam...

In this case there are two ways t deal with it. The probably saner
approach is to use a function like fgets() to read the input as a
string, analyze this string for a number you are prepared to accept,
and then to use e.g. strtol() to get at that number (which also allows
to do full error checking).

But since you're here just looking for a positive integer there's
the following alternative:

#include <stdio.h>

int main( void )
{
char buffer[10];
int x ;

do {
printf( "Please enter positive number\n" );
while ( scanf( "%9[^-+0-9]", buffer ) != 0 )
/* empty */ ;
} while ( scanf( "%d", &x ) != 1 || x <= 0 );
printf( "Got %d", x );
return 0;
}

The scanf() in the while loop consumes all non-numerical input
(except '+' and '-') and then the scanf() in the do loops
condition tries to read in an integer. If that works and the
number is positive the loop ends. If not we're back to square
one. But there's still the problem that you can't check if the
user input a number that's too large to be stored in an integer.
Such checks can only be done using the approach mentioned first.
And, of course, it also will return 123 for e.g. "asa%#123.12"
(and leaves everything following the "123" in the input buffer)
- I don't know if that's what you want.

Regards, Jens
--
\ Jens Thoms Toerring ___ (E-Mail Removed)
\__________________________ http://toerring.de
 
Reply With Quote
 
CBFalconer
Guest
Posts: n/a
 
      11-25-2006
Jens Thoms Toerring wrote:
> (E-Mail Removed) wrote:
>
>> I maybe reinvent the weel ...

>
> No, you just found out that scanf() is not very well suited for
> dealing with user input. You're definitely not the first and
> won't be the last
>
>> I'm trying to read positive integer number with the help of scanf,
>> if the input value is not positive number but negaive one zero or
>> char , i have to reread the input until I get the needed pos. number

>
>> I wrote the code , but it doesn't work the way it should : if i put
>> some char into input, the program goes infinite loop instead of
>> promting me to enter a new value.

>

.... snip code using scanf ...
>
> The scanf() in the while loop consumes all non-numerical input
> (except '+' and '-') and then the scanf() in the do loops
> condition tries to read in an integer. If that works and the
> number is positive the loop ends. If not we're back to square
> one. But there's still the problem that you can't check if the
> user input a number that's too large to be stored in an integer.
> Such checks can only be done using the approach mentioned first.
> And, of course, it also will return 123 for e.g. "asa%#123.12"
> (and leaves everything following the "123" in the input buffer)
> - I don't know if that's what you want.


ONLY be done? See the following. No buffers needed. Improved
since the published version. I am not completely happy with the
treatment of negative values in readxint. Also it needs to be
revised to use longs as the fundamental input mechanism. Note the
GPL licence.

/* ------------------------------------------------- *
* File txtinput.c *
* ------------------------------------------------- */

#include <limits.h> /* xxxx_MAX, xxxx_MIN */
#include <ctype.h> /* isdigit, isblank, isspace */
#include <stdio.h> /* FILE, getc, ungetc */
#include "txtinput.h"

/* For licensing restrictions (GPL) see readme.txt in:
* <http://cbfalconer.home.att.net/download/txtio.zip>
*
* These stream input routines are written so that simple
* conditionals can be used:
*
* if (readxint(&myint, stdin)) {
* do_error_recovery; normally_abort_to_somewhere;
* }
* else {
* do_normal_things; usually_much_longer_than_bad_case;
* }
*
* They allow overflow detection, and permit other routines to
* detect the character that terminated a numerical field. No
* string storage is required, thus there is no limitation on
* the length of input fields. For example, a number entered
* with a string of 1000 leading zeroes will not annoy these.
*
* The numerical input routines *NEVER* absorb a terminating
* char (including '\n'). Thus a sequence such as:
*
* err = readxint(&myint, stdin);
* flushln(stdin);
*
* will always consume complete lines, and after execution of
* readxint a further getc (or fgetc) will return the character
* that terminated the numeric field.
*
* They are also re-entrant, subject to the limitations of file
* systems. e.g interrupting readxint(v, stdin) operation with
* a call to readxwd(wd, stdin) would not be well defined, if
* the same stdin is being used for both calls. If ungetc is
* interruptible the run-time system is broken.
*
* Originally issued 2002-10-07
*
* Revised 2006-01-15 so that unsigned entry overflow (readxwd)
uses the normal C modulo (UINT_MAX + 1) operation. readxwd
still rejects an initial sign as an error.
*/

/* -------------------------------------------------------------
* Skip to non-blank on f, and return that char. or EOF The next
* char that getc(f) will return is unknown. Local use only.
*/
static int ignoreblks(FILE *f)
{
int ch;

do {
ch = getc(f);
} while ((' ' == ch) || ('\t' == ch));
/* while (isblank(ch)); */ /* for C99 */
return ch;
} /* ignoreblks */

/*--------------------------------------------------------------
* Skip all blanks on f. At completion getc(f) will return
* a non-blank character, which may be \n or EOF
*
* Skipblks returns the char that getc will next return, or EOF.
*/
int skipblks(FILE *f)
{
return ungetc(ignoreblks(f), f);
} /* skipblks */

/*--------------------------------------------------------------
* Skip all whitespace on f, including \n, \f, \v, \r. At
* completion getc(f) will return a non-blank character, which
* may be EOF
*
* Skipwhite returns the char that getc will next return, or EOF.
*/
int skipwhite(FILE *f)
{
int ch;

do {
ch = getc(f);
} while (isspace(ch));
return ungetc(ch, f);
} /* skipwhite */

/*--------------------------------------------------------------
* Read an unsigned value. Signal error for overflow or no
* valid number found. Returns 1 for error, 0 for noerror, EOF
* for EOF encountered before parsing a value.
*
* Skip all leading blanks on f. At completion getc(f) will
* return the character terminating the number, which may be \n
* or EOF among others. Barring EOF it will NOT be a digit. The
* combination of error, 0 result, and the next getc returning
* \n indicates that no numerical value was found on the line.
*
* If the user wants to skip all leading white space including
* \n, \f, \v, \r, he should first call "skipwhite(f);"
*
* Peculiarity: This specifically forbids a leading '+' or '-'.
*/
int readxwd(unsigned int *wd, FILE *f)
{
unsigned int value, digit;
int status;
int ch;

#define UWARNLVL (UINT_MAX / 10U)
#define UWARNDIG (UINT_MAX - UWARNLVL * 10U)

value = 0; /* default */
status = 1; /* default error */

ch = ignoreblks(f);

if (EOF == ch) status = EOF;
else if (isdigit(ch)) status = 0; /* digit, no error */

while (isdigit(ch)) {
digit = ch - '0';
if ((value > UWARNLVL) ||
((UWARNLVL == value) && (digit > UWARNDIG))) {
status = 1; /* overflow */
value -= UWARNLVL;
}
value = 10 * value + digit;
ch = getc(f);
} /* while (ch is a digit) */

*wd = value;
ungetc(ch, f);
return status;
} /* readxwd */

/*--------------------------------------------------------------
* Read a signed value. Signal error for overflow or no valid
* number found. Returns true for error, false for noerror. On
* overflow either INT_MAX or INT_MIN is returned in *val.
*
* Skip all leading blanks on f. At completion getc(f) will
* return the character terminating the number, which may be \n
* or EOF among others. Barring EOF it will NOT be a digit. The
* combination of error, 0 result, and the next getc returning
* \n indicates that no numerical value was found on the line.
*
* If the user wants to skip all leading white space including
* \n, \f, \v, \r, he should first call "skipwhite(f);"
*
* Peculiarity: an isolated leading '+' or '-' NOT immediately
* followed by a digit will return error and a value of 0, when
* the next getc will return that following non-digit. This is
* caused by the single level ungetc available.
*/
int readxint(int *val, FILE *f)
{
unsigned int value;
int status, negative;
int ch;

*val = value = 0; /* default */
status = 1; /* default error */
negative = 0;

ch = ignoreblks(f);

if (EOF != ch) {
if (('+' == ch) || ('-' == ch)) {
negative = ('-' == ch);
ch = ignoreblks(f); /* absorb any sign */
}

if (isdigit(ch)) { /* digit, no error */
ungetc(ch, f);
status = readxwd(&value, f);
ch = getc(f); /* This terminated readxwd */
}

if (0 == status) {
/* got initial digit and no readxwd overflow */
if (!negative && (value <= INT_MAX))
*val = value;
else if (negative && (value < UINT_MAX) &&
((value - 1) <= -(1 + INT_MIN)))
*val = -value;
else { /* overflow */
status = 1; /* do whatever the native system does */
if (negative) *val = -value;
else *val = value;
}
}
else if (negative) *val = -value;
else *val = value;
}
ungetc(ch, f);
return status;
} /* readxint */

/*-----------------------------------------------------
* Flush input through an end-of-line marker inclusive.
*/
void flushln(FILE *f)
{
int ch;

do {
ch = getc(f);
} while (('\n' != ch) && (EOF != ch));
} /* flushln */

/* End of txtinput.c */

--
Chuck F (cbfalconer at maineline dot net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net>

 
Reply With Quote
 
Jens Thoms Toerring
Guest
Posts: n/a
 
      11-25-2006
CBFalconer <(E-Mail Removed)> wrote:
> Jens Thoms Toerring wrote:
> > (E-Mail Removed) wrote:
> >> I'm trying to read positive integer number with the help of scanf,
> >> if the input value is not positive number but negaive one zero or
> >> char , i have to reread the input until I get the needed pos. number

> >
> >> I wrote the code , but it doesn't work the way it should : if i put
> >> some char into input, the program goes infinite loop instead of
> >> promting me to enter a new value.

> >

> ... snip code using scanf ...
> >
> > The scanf() in the while loop consumes all non-numerical input
> > (except '+' and '-') and then the scanf() in the do loops
> > condition tries to read in an integer. If that works and the
> > number is positive the loop ends. If not we're back to square
> > one. But there's still the problem that you can't check if the
> > user input a number that's too large to be stored in an integer.
> > Such checks can only be done using the approach mentioned first.
> > And, of course, it also will return 123 for e.g. "asa%#123.12"
> > (and leaves everything following the "123" in the input buffer)
> > - I don't know if that's what you want.


> ONLY be done? See the following. No buffers needed. Improved
> since the published version. I am not completely happy with the
> treatment of negative values in readxint. Also it needs to be
> revised to use longs as the fundamental input mechanism. Note the
> GPL licence.


<code snipped>

Thanks for the correction. What I actually wanted to warn the OP
about is that using only scanf() it's impossible to do full error
checking. As you have shown, using fgets() isn't the only method
that can be used here. And your code is a good example of what one
have to take into account to get it RIGHT Unfortunately, when
one has to read in floating point numbers it probably will become
very difficult (or impossible?) to work with a single look-ahead
character and I guess then some solution involving fgets() will be
necessary. Or do you have a nice function also for that case?

Regards, Jens
--
\ Jens Thoms Toerring ___ (E-Mail Removed)
\__________________________ http://toerring.de
 
Reply With Quote
 
CBFalconer
Guest
Posts: n/a
 
      11-25-2006
Jens Thoms Toerring wrote:
> CBFalconer <(E-Mail Removed)> wrote:
>

.... snip ...
>
>> ONLY be done? See the following. No buffers needed. Improved
>> since the published version. I am not completely happy with the
>> treatment of negative values in readxint. Also it needs to be
>> revised to use longs as the fundamental input mechanism. Note the
>> GPL licence.

>
> <code snipped>
>
> Thanks for the correction. What I actually wanted to warn the OP
> about is that using only scanf() it's impossible to do full error
> checking. As you have shown, using fgets() isn't the only method
> that can be used here. And your code is a good example of what one
> have to take into account to get it RIGHT Unfortunately, when
> one has to read in floating point numbers it probably will become
> very difficult (or impossible?) to work with a single look-ahead
> character and I guess then some solution involving fgets() will be
> necessary. Or do you have a nice function also for that case?


It can be built around the routines I showed. The only problem is
with exponential notation, otherwise 1 char lookahead does fine.
The problem case is: "1.23ex" which requires 2 char lookahead, or 2
level ungetc. scanf suffers from this too.

--
Chuck F (cbfalconer at maineline dot net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net>

 
Reply With Quote
 
stasgold@gmail.com
Guest
Posts: n/a
 
      11-25-2006
Thanks for reply.
I give it I try.
If this method is working only on flavours of unix , it's fine with me.

Ian Collins wrote:
> Richard Heathfield wrote:
> > Ian Collins said:
> >
> >
> >>Add
> >>
> >>fflush( stdin );

> >
> >
> > No, don't do that. The behaviour of fflush on streams open for input is
> > undefined.
> >

> Oops, so it is.
>
> I've always used the Solaris man page definition:
>
> "If stream points to an input stream or an update stream into
> which the most recent operation was input, that stream is
> flushed if it is seekable and is not already at end-of-file."
>
> --
> Ian Collins.


 
Reply With Quote
 
stasgold@gmail.com
Guest
Posts: n/a
 
      11-25-2006
Thanks Jens .

I have to use scanf() because of my task's constrains.
Your work-around works fine, if "fflush(stdin)" idea from previuos post
would fail,
i would surely go for it.

Thanks for the tip.

Jens Thoms Toerring wrote:
> (E-Mail Removed) wrote:
> > I maybe reinvent the weel ...

>
> No, you just found out that scanf() is not very well suited for
> dealing with user input. You're definitely not the first and
> won't be the last
>
> > I'm trying to read positive integer number with the help of scanf, if
> > the input value is not positive number but negaive one zero or char , i
> > have to reread the input until I get the needed pos. number

>
> > I wrote the code , but it doesn't work the way it should : if i put
> > some char into input, the program goes infinite loop instead of
> > promting me to enter a new value.

>
> > Please shed some light on what is the problem. thanks

>
> > #include <stdio.h>

>
> > int main()

>
> int main( void )
>
> tends to be better since your main() function doesn't take any input.
>
> > {
> > int n;
> > while (1)
> > {
> > printf("Please enter positive number \n");
> > if ( scanf("%d",&n)!=1 || n<=0 )

>
> The problem you're facing is that when scanf() doesn't find what
> it's looking for it won't read anything at all. So if the user inputs
> something that doesn't start with a number (with optionally a '+' or
> '-' in front of it) it stops immediately and nothing of the users
> input gets consumed. When scanf() then tries to read again it still
> finds the same characters it couldn't deal with already the last time
> and bails out again. Repeat at nauseam...
>
> In this case there are two ways t deal with it. The probably saner
> approach is to use a function like fgets() to read the input as a
> string, analyze this string for a number you are prepared to accept,
> and then to use e.g. strtol() to get at that number (which also allows
> to do full error checking).
>
> But since you're here just looking for a positive integer there's
> the following alternative:
>
> #include <stdio.h>
>
> int main( void )
> {
> char buffer[10];
> int x ;
>
> do {
> printf( "Please enter positive number\n" );
> while ( scanf( "%9[^-+0-9]", buffer ) != 0 )
> /* empty */ ;
> } while ( scanf( "%d", &x ) != 1 || x <= 0 );
> printf( "Got %d", x );
> return 0;
> }
>
> The scanf() in the while loop consumes all non-numerical input
> (except '+' and '-') and then the scanf() in the do loops
> condition tries to read in an integer. If that works and the
> number is positive the loop ends. If not we're back to square
> one. But there's still the problem that you can't check if the
> user input a number that's too large to be stored in an integer.
> Such checks can only be done using the approach mentioned first.
> And, of course, it also will return 123 for e.g. "asa%#123.12"
> (and leaves everything following the "123" in the input buffer)
> - I don't know if that's what you want.
>
> Regards, Jens
> --
> \ Jens Thoms Toerring ___ (E-Mail Removed)
> \__________________________ http://toerring.de


 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
difference between scanf("%i") and scanf("%d") ??? perhaps bug inVS2005? =?ISO-8859-1?Q?Martin_J=F8rgensen?= C Programming 18 05-02-2006 10:53 AM
scanf (yes/no) - doesn't work + deprecation errors scanf, fopen etc. =?ISO-8859-1?Q?Martin_J=F8rgensen?= C Programming 185 04-03-2006 02:49 PM
confused about behaviour of scanf Lalatendu Das C Programming 33 01-04-2006 05:27 AM
Scanf Behaviour sajjanharudit@gmail.com C Programming 7 12-30-2005 07:20 PM
Correct behaviour of scanf and sscanf Rob Thorpe C Programming 6 03-15-2005 04:25 PM



Advertisments