如何使用SCANF仅允许特定格式的输入?

时间:2018-10-29 20:35:10

标签: c formatting scanf

我希望用户将3个不同的年龄放在方括号中,并用逗号分隔。因此它看起来像这样{int,int,int}。

我尝试过:

int a,b,c;
if(scanf("{ %d , %d , %d }", &a,&b,&c)!=3){
printf("Bad format");
}

但是它不能正确拒绝像{ 1, 2, 3,这样的输入

我要允许:

{1,2,3}
{ 1 , 2 ,3 }
{ 1 ,    2 ,       3}

并拒绝:

{1,2,3,4}
{1 2 3 4}
1234
1 2 3 4

谢谢。

2 个答案:

答案 0 :(得分:0)

读取输入的并测试其是否包含诸如" }"之类的尾随字符的格式的一种简单方法是将fgets()sscanf()" %n"

"%n"记录到目前为止的扫描偏移量-如果它偏移了那么远。

// 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);
  }
}

上面的缺点。

int未检测到溢出。

#define INT_TEXT_SIZE 11假定为32位或更小的intint的文本需求大约为log2(整数位大小),因此可以从here中提取INT_DEC_TEXT的代码。

答案 1 :(得分:0)

(有些答案是有根据的。对不起。我有意见。)

scanf确实不是精确验证输入的理想工具。特别是,它通常不能区分换行符和其他空格,这一事实使得很难严格验证面向行的输入。

另一方面,如果您有所有的标点符号,也许您应该准备接受更多自由格式的输入。用户为什么不应该在两行甚至五行中输入数据:

{
  1,
  2,
  3
}

(或pythonesquely:

{ 1
, 2
, 3
}

:-))

除非您竭尽全力禁止使用它们,或者使用此处通常建议的fgets/sscanf解决方法,否则上述所有内容都将被接受。您可能永远不知道这会让某些用户感到高兴。

但是,还有另一个问题可能值得尝试解决。在这里,您要确保三元组正确地以短括号}终止,而不能仅仅通过将}放入模式中来做到这一点。如果需要验证最后一次转换后的字符,则需要将该字符转换为一次字符(以使其成为最后一次转换)。否则,scanf只会在输入流中保留不匹配的输入字符,并报告所有数据转换均已成功。

所以您可以使用类似这样的东西:

int a,b,c;
char brace;
if (scanf("{ %d , %d , %d %c", &a, &b, &c, &brace) != 4
    || brace != '}') {
  printf("Bad format");
}

这样可以保证存在一个},可能在其后跟空白。但是, 不保证}是行中的最后一件事。由于它小心翼翼地不跳过模式后的空格,因此可以通过使用fgets(它将读取直到并包括尾随换行符)读取该行的其余部分来检查是否为空,然后检查该字符请阅读以确保它们都满足isspace()

或者您可以让用户将换行符放置在他们想要的任何地方。