checking and verifying input line in a C program

H

Hulo

In a C program I am required to enter three numbers (integers) e.g. 256
7 5 on execution of the program.

C:\> 256 7 5

There should be spaces between the three numbers and on pressing
"enter", further processing is done. The problem requires me to check
whether three numbers have actually been entered in the input line and
to warn if less or more numbers have been entered.

The actual program is required to do something else. But I'm stuck at
this initial input verification. If I use 3 variables x,y & z to store
these numbers and use scanf() e.g. scanf("%d%d%d",&x,&y,&z) then the
problem remains that the input process is not interactive. If excess
arguments are given, anything beyond the third argument will be
discarded by scanf(). If someone gives less than 3 arguments and press
"enter", the cursor goes to the next line and waits.

I guess, the problem requies me to make this process more interactive.
It will warn if less than 3 or more than 3 numbers are keyed by the
user in the input line. Evidently scanf() cannot handle it.

How to implement it in the least complicated manner?
 
V

Vladimir S. Oka

Hulo said:
In a C program I am required to enter three numbers (integers) e.g.
256 7 5 on execution of the program.

C:\> 256 7 5

There should be spaces between the three numbers and on pressing
"enter", further processing is done. The problem requires me to check
whether three numbers have actually been entered in the input line and
to warn if less or more numbers have been entered.

if you search this newsgroup, I believe that recently there was a thread
discussing somewhat similar problems (look for strtok()). See also my
comments below.
The actual program is required to do something else. But I'm stuck at
this initial input verification. If I use 3 variables x,y & z to store
these numbers and use scanf() e.g. scanf("%d%d%d",&x,&y,&z) then the
problem remains that the input process is not interactive. If excess
arguments are given, anything beyond the third argument will be
discarded by scanf(). If someone gives less than 3 arguments and press
"enter", the cursor goes to the next line and waits.

Do not use scanf() (for detailed discussion of why, search this
newsgroup). If you have to, use fgets()/sscanf() combination. Look them
up in your manual/textbook.
I guess, the problem requies me to make this process more interactive.
It will warn if less than 3 or more than 3 numbers are keyed by the
user in the input line. Evidently scanf() cannot handle it.

How to implement it in the least complicated manner?

You could also try to get your line with fgets() (again, do not use
gets() -- it's dangerous as it can create buffer overflows, see
elsewhere in this newsgroup for detailed discussion), and process it
with strtok(). Look it up in your manual/textbook, and there was also a
thread here recently which discussed strtok() in some detail.

Once you read up on these, give it a go by writing a simple program. If
you get specific problems in your code, do come back here and ask.

Cheers

Vladimir
 
N

Nick Keighley

Hulo said:
In a C program I am required to enter three numbers (integers) e.g. 256
7 5 on execution of the program.

C:\> 256 7 5

There should be spaces between the three numbers and on pressing
"enter", further processing is done. The problem requires me to check
whether three numbers have actually been entered in the input line and
to warn if less or more numbers have been entered.

The actual program is required to do something else. But I'm stuck at
this initial input verification. If I use 3 variables x,y & z to store
these numbers and use scanf() e.g. scanf("%d%d%d",&x,&y,&z) then the
problem remains that the input process is not interactive. If excess
arguments are given, anything beyond the third argument will be
discarded by scanf(). If someone gives less than 3 arguments and press
"enter", the cursor goes to the next line and waits.

I guess, the problem requies me to make this process more interactive.
It will warn if less than 3 or more than 3 numbers are keyed by the
user in the input line. Evidently scanf() cannot handle it.

How to implement it in the least complicated manner?

use fgets() to read the whole line, then sscanf() to parse the line.
sscanf()
indicates how many items it read.
 
M

Mark McIntyre

The actual program is required to do something else. But I'm stuck at
this initial input verification. If I use 3 variables x,y & z to store
these numbers and use scanf() e.g. scanf("%d%d%d",&x,&y,&z) then the
problem remains that the input process is not interactive. If excess
arguments are given, anything beyond the third argument will be
discarded by scanf(). If someone gives less than 3 arguments and press
"enter", the cursor goes to the next line and waits.

fgets, then sscanf, then handle too many/ not enough data points.
Mark McIntyre
 
P

Peter Nilsson

Hulo said:
In a C program I am required to enter three numbers (integers) e.g. 256
7 5 on execution of the program.

C:\> 256 7 5

There should be spaces between the three numbers and on pressing
"enter", further processing is done. The problem requires me to check
whether three numbers have actually been entered in the input line and
to warn if less or more numbers have been entered.

You've hit what is a surprisingly non trivial thing to do in C. A truly
robust bullet proof program will allow the user to do some things like
entering 00000000000000000000000000000000000000012,
potentially exhausting any fixed with input buffer. The fgets/sscanf
combination can fail because of that and sscanf's %d invokes
undefined behaviour if it receives an integer which is outside the
range of int.

One way is to write a routine that excepts an arbitrary large
input line by dynamically allocating space for it (samples have been
posted to clc on numerous occasions), and then parse that input
using strtol (making use of the 'end' pointer).

If you're willing to flag multiple leading zeros as an error, then you
can fix your buffer to a compile time fixed limit. The following is
not checked, but is a rough guide...

#define BIG_ENOUGH_FOR_INT \
( (int) (sizeof(int) * CHAR_BIT + 11) / 3) )

const char format[100 + BIG_ENOUGH_FOR_INT*3];
char n1[BIG_ENOUGH_FOR_INT];
char n2[BIG_ENOUGH_FOR_INT];
char n3[BIG_ENOUGH_FOR_INT];
char c;
int r;
long l1, l2, l3;

sprintf(format, "%%%d[0123456789]"
"%%*[ \t]%%%d[0123456789]"
"%%*[ \t]%%%d[0123456789]"
"%%c",
BIG_ENOUGH_FOR_INT,
BIG_ENOUGH_FOR_INT,
BIG_ENOUGH_FOR_INT);

scanf("%*[ \t]"); /* ignore leading whitespace */
r = scanf(format, n1, n2, n3, &c);
if (r == 3 || r == 4 && c == '\n')
{
errno = 0;
l1 = strtol(n1, NULL, 10);
l2 = strtol(n2, NULL, 10);
l3 = strtol(n3, NULL, 10);
... further validations...
}

Another alternative is to write your own routine to parse input that
reads a
single integer and does the conversion to int (avoiding overflow),
without
ignoring leading whitespace.
 
S

serrand

Peter said:
You've hit what is a surprisingly non trivial thing to do in C. A truly
robust bullet proof program will allow the user to do some things like
entering 00000000000000000000000000000000000000012,
potentially exhausting any fixed with input buffer. The fgets/sscanf
combination can fail because of that and sscanf's %d invokes
undefined behaviour if it receives an integer which is outside the
range of int.

One way is to write a routine that excepts an arbitrary large
input line by dynamically allocating space for it (samples have been
posted to clc on numerous occasions), and then parse that input
using strtol (making use of the 'end' pointer).

If you're willing to flag multiple leading zeros as an error, then you
can fix your buffer to a compile time fixed limit. The following is
not checked, but is a rough guide...
#define BIG_ENOUGH_FOR_INT \
( (int) (sizeof(int) * CHAR_BIT + 11) / 3) )
( (int) (sizeof(int) * CHAR_BIT + 5) / 3) )
is enough for signed, isn't it?
I wonder why you are using two more places...
const char format[100 + BIG_ENOUGH_FOR_INT*3];
char n1[BIG_ENOUGH_FOR_INT];
char n2[BIG_ENOUGH_FOR_INT];
char n3[BIG_ENOUGH_FOR_INT];

Xavier
 
C

CBFalconer

Peter said:
.... snip ...

You've hit what is a surprisingly non trivial thing to do in C. A
truly robust bullet proof program will allow the user to do some
things like entering 00000000000000000000000000000000000000012,
potentially exhausting any fixed with input buffer. The fgets/sscanf
combination can fail because of that and sscanf's %d invokes
undefined behaviour if it receives an integer which is outside
the range of int.

One way is to write a routine that excepts an arbitrary large
input line by dynamically allocating space for it (samples have
been posted to clc on numerous occasions), and then parse that
input using strtol (making use of the 'end' pointer).

If you're willing to flag multiple leading zeros as an error, then
you can fix your buffer to a compile time fixed limit.
.... snip ...

Try these, after suitable #includes. No buffers needed. Build int
input routines around them. The only nuisance for that is a
guaranteed method of negating values without creating an integer
overflow.

/* --------------------------------------------------------------
* 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,
* because it violates the principle of leaving the terminating
* char in the input stream.
*/
static int ignoreblks(FILE *f)
{
int ch;

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

/*--------------------------------------------------------------
* 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 */

--
"The power of the Executive to cast a man into prison without
formulating any charge known to the law, and particularly to
deny him the judgement of his peers, is in the highest degree
odious and is the foundation of all totalitarian government
whether Nazi or Communist." -- W. Churchill, Nov 21, 1943
 
D

Dave Thompson

Peter Nilsson wrote:
( (int) (sizeof(int) * CHAR_BIT + 5) / 3) )
is enough for signed, isn't it?
I wonder why you are using two more places...
That's enough for _unsigned_. Signed requires one more.

But he has two more. In some situations that might allow for a decimal
point (redundant for integer) or a currency symbol, but for just a
number it isn't needed.

- David.Thompson1 at worldnet.att.net
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top