Only number input thru scanf()

S

Sandy Beech

Hello,

I want to restrict the input from the user only to numbers. How can i
do that with scanf()? Is there any other way i can do that ? Also i want
the scanf() to accept even the carriage return without entering
anything. How can i acheive that? Please help me out.
 
M

Michael Angelo Ravera

Hello,

 I want to restrict the input from the user only to numbers. How can i
do that with scanf()? Is there any other way i can do that ? Also i want
the scanf() to accept even the carriage return without entering
anything. How can i acheive that? Please help me out.

You need to create a specialized entry function or do something OS
specific in order to restrict input. In most cases, these specialized
entry functions simply ignore and refuse to echo input characters that
are not appropriate for the type of data input (and maybe even beep,
flash, or shout, if the user types something else).

In any case, scanf() is not your friend on this. I'm not saying that
you can't do what you want with some repeated and creative application
of scanf(), but scanf() certianly isn't written for this purpose.
 
F

Felix Palmen

* Sandy Beech said:
I want to restrict the input from the user only to numbers. How can i
do that with scanf()? Is there any other way i can do that ? Also i want
the scanf() to accept even the carriage return without entering
anything. How can i acheive that? Please help me out.

You can't. Either check user input later, optionally telling the user
what went wrong and letting him repeat the input, or sacrifice
portability and do it yourself, using things like conio on windows or
raw terminal mode on *nix.

Regards,
Felix
 
P

Peter Nilsson

Sandy Beech said:
 I want to restrict the input from the user only to numbers.
How can i do that with scanf()?

Effectively, you can't.
Is there any other way i can do that ? Also i want
the scanf() to accept even the carriage return without
entering anything.

Use fgets, then sscanf, or better still strtol.
 
K

Keith Thompson

Sandy Beech said:
I want to restrict the input from the user only to numbers. How can i
do that with scanf()? Is there any other way i can do that ? Also i want
the scanf() to accept even the carriage return without entering
anything. How can i acheive that? Please help me out.

What exactly do you mean by "restrict"? If the user types something
other than a number, what should happen?
 
P

Peter 'Shaggy' Haywood

Groovy hepcat Felix Palmen was jivin' in comp.lang.c on Wed, 3 Nov 2010
9:17 am. It's a cool scene! Dig it.
You can't.

Nonsense! You certainly can do that. Of course, scanf() may not be the
best function for the job. The OP should read the FAQ for more on this.
I'd maybe use fgets() followed by sscanf(). This would also provide a
trivial way to determine whether the input contains only a newline.
But to give you some idea what can be done using only scanf() for
input, consider the following code:

#include <stdio.h>

int main(void)
{
int nfields, num;
char dummy[2];

fputs("Please enter a decimal integer: ", stdout);
fflush(stdout);

nfields = scanf("%d%1[^\n]", &num, dummy);
while(1 != nfields)
{
puts("Invalid input! Please try again.");
fputs("Enter a decimal integer (and nothing else): ", stdout);
fflush(stdout);

/* Consume remaining line of input. */
while(0 != scanf("%*[^\n]"))
;

nfields = scanf("%d%1[^\n]", &num, dummy);
}

printf("num = %d\n", num);
}

This code first prompts for a number, then attempts to read in two
fields: a decimal integer and a single character that is not a newline
(as indicated by the %1[^\n] conversion specifier). However, we want it
to fail to read the second field. There are several scenarios that can
be encountered:

1) no fields are read in, meaning there are no digits at the start of
the input, scanf() returns 0, and we enter the loop to try again (after
consuming the remaining line of input so we don't get stuck in an
endless loop);

2) a numerical field and an extra character field are read in, meaning
there are digits at the start of the input but there is some non-digit
character(s) after that, scanf() returns 2, and the loop is entered to
try again;

3) a numerical field is read, but no other characters are present, and
scanf() returns 1, meaning that only digits were entered, which is what
we want, so we don't enter the loop.

The last printf() statement then tells us what number was entered, and
the program ends.
To determine whether a newline has been entered is also trivial using
scanf(). The %c conversion specifier reads the next character, even if
it is white space. Regardez:

char ch;
scanf("%c", &ch);
if('\n' == ch)
{
/* Newline has been entered. */
}

Or you could use the %1[\n] conversion specifier to force scanf() to
read only a single newline. Note that this treats the input as a
string, even if it is only one character long (which it will be since
any character other than a single newline will be rejected), so you
need an array of at least two bytes (for the input and null).

char s[2];
if(1 == scanf("%1[\n]", s))
{
/* Newline has been entered. */
}
 
D

David RF

Hello,

 I want to restrict the input from the user only to numbers. How can i
do that with scanf()? Is there any other way i can do that ? Also i want
the scanf() to accept even the carriage return without entering
anything. How can i acheive that? Please help me out.

If you are under a unix shell you can use termios

A little example (never tested in real production, any advice is
welcomed)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <termios.h>
#include <unistd.h>

#define KEY_TAB 0x0009 /* 9 */
#define KEY_ENTER 0x000a /* 10 */
#define KEY_ESCAPE 0x001b /* 27 */
#define KEY_BACKSPACE 0x007f /* 127 */

#define KEY_DELETE 0x0100 /* 256 */
#define KEY_HOME 0x0101
#define KEY_END 0x0102
#define KEY_REPAG 0x0103
#define KEY_AVPAG 0x0104
#define KEY_UP 0x0105
#define KEY_DOWN 0x0106
#define KEY_LEFT 0x0107
#define KEY_RIGHT 0x0108

/* Macros terminal */
#define cursorforward(x) printf("\033[%dC", (x))
#define cursorbackward(x) printf("\033[%dD", (x))
#define savecursor() printf("\033[s")
#define unsavecursor() printf("\033[u")
/* END Macros terminal */

static struct termios term, oterm;

static int getch(void);
static int kbhit(void);
static int kbesc(void);
static int kbget(void);

static int getch(void)
{
int c = 0;

tcgetattr(0, &oterm);
memcpy(&term, &oterm, sizeof(term));
term.c_lflag &= ~(ICANON | ECHO);
term.c_cc[VMIN] = 1;
term.c_cc[VTIME] = 0;
tcsetattr(0, TCSANOW, &term);
c = getchar();
tcsetattr(0, TCSANOW, &oterm);
return c;
}

static int kbhit(void)
{
int c = 0;

tcgetattr(0, &oterm);
memcpy(&term, &oterm, sizeof(term));
term.c_lflag &= ~(ICANON | ECHO);
term.c_cc[VMIN] = 0;
term.c_cc[VTIME] = 1;
tcsetattr(0, TCSANOW, &term);
c = getchar();
tcsetattr(0, TCSANOW, &oterm);
if (c != -1) ungetc(c, stdin);
return ((c != -1) ? 1 : 0);
}

static int kbesc(void)
{
int c;

if (!kbhit()) return KEY_ESCAPE;
c = getch();
if ((c == '[') || (c == 'O')) {
switch (getch()) {
case 'A':
c = KEY_UP;
break;
case 'B':
c = KEY_DOWN;
break;
case 'C':
c = KEY_LEFT;
break;
case 'D':
c = KEY_RIGHT;
break;
case 'F':
c = KEY_END;
break;
case 'H':
c = KEY_HOME;
break;
case '1':
if (getch() == '~') c = KEY_HOME; else c = 0;
break;
case '3':
if (getch() == '~') c = KEY_DELETE; else c = 0;
break;
case '4':
if (getch() == '~') c = KEY_END; else c = 0;
break;
case '5':
if (getch() == '~') c = KEY_REPAG; else c = 0;
break;
case '6':
if (getch() == '~') c = KEY_AVPAG; else c = 0;
break;
default:
c = 0;
break;
}
} else {
c = 0;
}
if (c == 0) while (kbhit()) getch();
return c;
}

static int kbget(void)
{
int c;

c = getch();
return (c == KEY_ESCAPE) ? kbesc() : c;
}

char *inputfn(size_t size, int (*fn)(int))
{
int c, pos = 0, len = 0;
char *entry;

entry = calloc(size + 1, sizeof(char));
if (entry == NULL) {
fprintf(stderr, "Aargh ...");
exit(EXIT_FAILURE);
}
for (;;) {
c = kbget();
if (
c == KEY_ENTER || c == KEY_ESCAPE ||
c == KEY_TAB || c == KEY_REPAG || c == KEY_AVPAG ||
c == KEY_UP || c == KEY_DOWN
) break;
if (c == KEY_BACKSPACE) {
if (pos > 0) {
pos--;
putchar('\b');
c = KEY_DELETE;
} else continue;
}
if (c == KEY_DELETE) {
if (pos < len) {
memmove(&entry[pos], &entry[pos + 1], (size_t)(len - pos));
len--;
savecursor();
printf("%s ", &entry[pos]);
unsavecursor();
}
} else
if (c == KEY_HOME) {
if (pos > 0) {
cursorbackward(pos);
pos = 0;
}
} else
if (c == KEY_END) {
if (pos < len) {
cursorforward(len - pos);
pos = len;
}
} else
if (c == KEY_RIGHT) {
if (pos > 0) {
pos--;
putchar('\b');
}
} else
if (c == KEY_LEFT) {
if (pos < len) {
putchar(entry[pos++]);
}
} else
if (fn && fn((unsigned char)c)) {
/* Si es el primer caracter de la cadena borra todo */
/* if first char delete all */
if (pos == 0 && len > 1) {
memset(entry, 0, (size_t)len);
savecursor();
printf("%*s", len, "");
unsavecursor();
len = 0;
}
if (pos == len) {
if (len == size) continue;
len++;
}
entry[pos++] = (char)c;
putchar(c);
}
}
return entry;
}

int main(void)
{
char *entry;

printf("Input number: ");
entry = inputfn(5, isdigit);
/* where 5 is max input and isdigit a filter (you can use any ctype
or you own) */
printf("\nYour number is %s\n", entry);
free(entry);

return 0;
}
 
K

Keith Thompson

Peter 'Shaggy' Haywood said:
Groovy hepcat Felix Palmen was jivin' in comp.lang.c on Wed, 3 Nov 2010
9:17 am. It's a cool scene! Dig it.


Nonsense! You certainly can do that. Of course, scanf() may not be the
best function for the job. The OP should read the FAQ for more on this.
[snip]

It depends on just what the OP is trying to accomplish. In particular,
it depends on what the OP means by "restrict". (I asked, and I haven't
seen a response.)

Here's an example of something the OP *might* have meant, something
that can't be done in portable and/or standard C. I once wrote a
numeric input routine that would accept only well-formed numeric
input. If the user typed a character that could not be part of
a number, it was discarded, not even echoed. For example, if the
user typed "12xyz34", only "1234" would appear on the screen, and
the input value would be 1234.

If you're not going to play that kind of trick to disallow invalid
input, you have to decide how to respond when the user enters something
other than a number. I suspect the OP hasn't thought about that.
 
D

David Thompson

But to give you some idea what can be done using only scanf() for
input, consider the following code:
nfields = scanf("%d%1[^\n]", &num, dummy);
while(1 != nfields)
{
puts("Invalid input! Please try again.");
fputs("Enter a decimal integer (and nothing else): ", stdout);
fflush(stdout);
Loops infinitely for persistent I/O error or EOF -- although this
clearly is intended only to handle input from an interactive device
like a keyboard, and keyboard EOF often (usually?) isn't sticky.
(Also the message wording might be off for this case.)
/* Consume remaining line of input. */
while(0 != scanf("%*[^\n]"))
;
Don't need while, just one call since %dftwidth[ = unbounded.
(Like %s but unlike %c which defaults to 1.)
nfields = scanf("%d%1[^\n]", &num, dummy);
}
Personally I would either put the repeated scanf attempt in the while
condition, or make this an n.5 loop explicitly (with break), rather
than duplicating the code. But that's 'merely' style.
printf("num = %d\n", num);
}
<snip rest>
 

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,054
Latest member
TrimKetoBoost

Latest Threads

Top