scanf ("% [^ \ n] s", a) vs gets (a) - PullRequest
       65

scanf ("% [^ \ n] s", a) vs gets (a)

9 голосов
/ 18 ноября 2011

Мне сказали, что scanf не должен использоваться, когда пользователь вводит строку. Вместо этого, перейдите к get () большинством экспертов, а также пользователями StackOverflow. Я никогда не спрашивал об этом в StackOverflow, почему нельзя использовать scanf over get для строк. Это не фактический вопрос, но ответ на этот вопрос очень ценится.

Теперь перейдем к актуальному вопросу. Я сталкивался с этим типом кода -

scanf("%[^\n]s",a); 

Это читает строку, пока пользователь не введет символ новой строки, считая пробелы также как строку.

Есть ли проблема, если я использую

scanf("%[^\n]s",a);

вместо получает?

Is становится более оптимизированным, чем функция scanf, как это звучит, get предназначен исключительно для обработки строк. Пожалуйста, дайте мне знать об этом.

Обновление

Эта ссылка помогла мне лучше понять ее.

Ответы [ 2 ]

7 голосов
/ 18 ноября 2011

gets(3) опасно и его следует избегать любой ценой. Я не могу представить, чтобы использование gets(3) было , а не недостатком безопасности.

scanf(3) s %s также опасно - вы должны использовать спецификатор "field width", чтобы указать размер выделенного буфера. Без ширины поля эта процедура так же опасна, как gets(3):

char name[64];
scanf("%63s", name);

Библиотека GNU C предоставляет модификатор a до %s, который выделяет вам буфер. Это непереносимое расширение, вероятно, менее трудно использовать правильно:

   The GNU C library supports a nonstandard extension that
   causes the library to dynamically allocate a string of
   sufficient size for input strings for the %s and %a[range]
   conversion specifiers.  To make use of this feature, specify
   a as a length modifier (thus %as or %a[range]).  The caller
   must free(3) the returned string, as in the following
   example:

       char *p;
       int n;

       errno = 0;
       n = scanf("%a[a-z]", &p);
       if (n == 1) {
           printf("read: %s\n", p);
           free(p);
       } else if (errno != 0) {
           perror("scanf");
       } else {
           fprintf(stderr, "No matching characters\n"):
       }

   As shown in the above example, it is only necessary to call
   free(3) if the scanf() call successfully read a string.
4 голосов
/ 10 ноября 2016

Во-первых, не ясно, что делает s в вашей строке формата. Часть %[^\n] является самодостаточным спецификатором формата. Это не модификатор для формата %s, как вы, кажется, верите. Это означает, что строка формата "%[^\n]s" будет интерпретироваться scanf как два независимых спецификатора формата: %[^\n], за которым следует одиночный s. Это заставит scanf прочитать все, пока не встретится \n (оставив \n непрочитанным), а затем потребует, чтобы следующий входной символ был s. Это просто не имеет никакого смысла. Никакие входные данные не будут соответствовать такому противоречивому формату.

Во-вторых, очевидно, что имелось ввиду scanf("%[^\n]", a). Это несколько близко к [больше не доступно] gets (или fgets), но это не то же самое. scanf требует, чтобы каждый спецификатор формата соответствовал хотя бы одному входному символу. scanf завершится ошибкой и прервется, если он не сможет соответствовать никаким входным символам для запрошенного спецификатора формата. Это означает, что scanf("%[^\n]",a) не способен читать пустые строки ввода, то есть строки, содержащие символ \n сразу. Если вы введете такую ​​строку в вышеприведенную scanf, она вернет 0, чтобы указать на ошибку, и оставит a без изменений. Это сильно отличается от того, как работают типичные функции ввода на основе строки.

(Это довольно удивительный и, казалось бы, нелогичный формат %[]. Лично я предпочел бы, чтобы %[] мог сопоставлять пустые последовательности и создавать пустые строки, но это не так, как работает стандартный scanf. )

Если вы хотите читать ввод построчно, fgets - ваш лучший вариант.

...