Как разрешить только определенный формат ввода с помощью SCANF? - PullRequest
0 голосов
/ 29 октября 2018

Я хочу, чтобы мой пользователь поместил 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 голосов
/ 30 октября 2018

(Часть этого ответа является самоуверенным. Извините. У меня есть мнения.)

scanf действительно не идеальный инструмент для точной проверки ввода. В частности, тот факт, что он обычно не различает символы новой строки и другие пробелы, делает действительно очень трудным проверку строго линейно-ориентированного ввода.

С другой стороны, если у вас есть вся эта пунктуация, возможно, вы должны быть готовы принять более произвольный ввод. Почему пользователь не должен вводить данные в две строки или даже в пять строк:

{
  1,
  2,
  3
}

(или питонски:

{ 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().

Или вы можете просто позволить пользователю ставить переводы строк, где они захотят.

0 голосов
/ 30 октября 2018

Простой способ прочитать строку ввода и проверить ее на формат, который включает в себя завершающие символы, такие как " }", - это использовать 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-разрядную или меньшую int. Текстовые потребности int примерно равны log2 (целочисленный битовый размер), поэтому код может INT_DEC_TEXT из здесь .

...