Reputation: 11
I want my user to put 3 different ages into brackets and separate them with comma. So it would look like this {int,int,int}.
I tried:
int a,b,c;
if(scanf("{ %d , %d , %d }", &a,&b,&c)!=3){
printf("Bad format");
}
but it doesn't correctly reject an input like { 1, 2, 3,
I want to allow:
{1,2,3}
{ 1 , 2 ,3 }
{ 1 , 2 , 3}
and reject:
{1,2,3,4}
{1 2 3 4}
1234
1 2 3 4
etc.
Thanks.
Upvotes: 0
Views: 1516
Reputation: 153602
An easy way to read a line of input and test it for a format that includes trailing characters like " }"
is to use fgets()
and sscanf()
with " %n"
.
"%n"
records the offset of the scan so far - if it made it that far.
// Sample code
#define INT_TEXT_SIZE 11
#define FMT3 "{ %d , %d , %d }"
#define LINE_EXPECTED_MAX_SIZE (sizeof FMT3 + 3*INT_TEXT_SIZE);
// Use 2x expected max size to allow for extra spaces, leading 0, etc.
char buf[LINE_EXPECTED_MAX_SIZE * 2 + 1];
if (fgets(buf, sizeof buf, stdin)) {
int n = 0;
sscanf(buf, FMT3 " %n", &a, &b, &c, &n);
// if scanning was incomplete or extra junk at the end...
if (n == 0 || buf[n]) {
printf("Bad format <%s>", buf);
} else {
printf("Succcess %d %d %d\n", a,b,c);
}
}
Shortcomings with the above.
int
overflow not detected.
#define INT_TEXT_SIZE 11
assumes 32-bit or smaller int
. The text needs of an int
are about log2(integer bit size) so code could INT_DEC_TEXT
from here.
Upvotes: 0
Reputation: 241811
(Some of this answer is opinionated. Sorry. I have opinions.)
scanf
is really not the ideal tool for precise verification of input. In particular, the fact that it does not normally distinguish between newlines and other whitespace makes it really very difficult to validate strictly line-oriented input.
On the other hand, if you've got all that punctuation in there, maybe you should be prepared to accept more free-form input. Why shouldn't the user enter the data on two lines, or even five lines:
{
1,
2,
3
}
(Or pythonesquely:
{ 1
, 2
, 3
}
:-) )
Unless you go to heroic lengths to forbid them, or use the fgets/sscanf
workaround usually suggested here, all of the above will be accepted. Which might make some user happy, you never know.
However, there is another issue which is possibly worth trying to solve. Here, you want to ensure that the triple is correctly terminated with a close brace }
, and you can't do that by just putting }
into the pattern. If you need to validate a character which comes after the the last conversion, you need to make that character a conversion (so that it becomes the last conversion). Otherwise, scanf
will simply leave the unmatched input character in the input stream and report that all data conversions were successful.
So you could use something like this:
int a,b,c;
char brace;
if (scanf("{ %d , %d , %d %c", &a, &b, &c, &brace) != 4
|| brace != '}') {
printf("Bad format");
}
That guarantees that there is a }
, possibly preceded by whitespace. However, it
doesn't guarantee that the }
is the last thing on the line. Since it carefully does not skip whitespace after the pattern, you could check that the rest of the line is empty by reading it with fgets
(which will read up to and including the trailing newline character), and then checking the characters read to make sure they all satisfy isspace()
.
Or you could just let the user put newlines wherever they want to.
Upvotes: 1