Как будет работать использование scanf?

Как лучше всего имитировать функциональность gets с помощью scanf?

Вот моя текущая попытка

 int main()
 {
    char cvalue[20];   //char array to store input string
    int iloop=0;      //integer variable for loop

    for(iloop=0;iloop<20;iloop++)     // for loop to get the string char by char
    {
        scanf("%c",&cvalue[iloop]); //getting input
        if(cvalue[iloop]=='\n')    //if input is newline skip further looping
        break;
    }                    // end of loop

    cvalue[iloop]='\0';         //set end of the character for given input
    printf("%s",cvalue);        //printing the given string
    return 0;
}

person Vijayakumar    schedule 14.06.2013    source источник
comment
Пожалуйста, найдите время, чтобы узнать, как форматировать ваши вопросы. Я также сформулировал ваш вопрос, чтобы он лучше соответствовал формату вопроса SO. Сначала четко задайте вопрос, затем покажите свою текущую попытку.   -  person Benjamin Gruenbaum    schedule 14.06.2013


Ответы (3)


Вы можете использовать scanf таким образом, чтобы работать как gets

scanf("%[^\n]",&a);
person Jayram    schedule 14.06.2013
comment
Кажется, что это не может использовать \n на входе, например gets(). gets() читает '\n' , но не сохраняет его в &a. - person chux - Reinstate Monica; 18.06.2013

Вы должны соблюдать обычные опасности gets().

Задача использования scanf() состоит в следующем:
1) обеспечить потребление \n. scanf("%[^\n]",... этого не делает.
2) Гарантия того, что str получает \0, если читается только \n.
3) Работа с ошибками EOF и ввода-вывода и возвратом 0.
4) Убедитесь, что начальные пробелы читать в str, так как scanf("%s" пропускает их.

#include <stdio.h>
// On success, the gets() returns str.
// If EOF encountered, the eof indicator is set (feof).
// If this happens before any characters could be read, 
//    pointer returned is a null pointer.
//  If a read error occurs, the error (ferror) is set
//    and a null pointer is also returned.
char *gets_via_scanf( char * str ) {
  // Reads characters from stdin & saves them into str until \n or the end-of-file.
  // \n, if found, is not copied into str.
  int retval = scanf("%[^\n]",str);  // %[ does not skip leading whitespace
  if (retval == EOF) return 0;
  if (retval == 0) {
    *str = '\0';  // Happens when users only types in \n
  }
  char ch;
  scanf("%c",&ch); // Consume leftover \n, could be done with getc()
  return str;
}
person chux - Reinstate Monica    schedule 18.06.2013

Ваша попытка на самом деле не имитирует gets(), поскольку gets() просто продолжает помещать байты в предоставленный буфер до тех пор, пока не будет достигнут конец строки. Вы должны понимать, что gets() опасен и его следует избегать. Он не предлагает никакой защиты от переполнения буфера. Так что имитировать его тоже сомнительно.

Учитывая это, ваша попытка имеет пару недостатков, которые я вижу. Во-первых, он возвращается к полному размеру входного буфера. Это не оставляет вам места для хранения терминатора NUL, если длина входной строки составляет 20 байт или больше. Это означает, что вы можете попытаться сохранить \0 в cvalue[20], что находится за границей массива. Вы можете исправить это, сократив цикл for на единицу:

    for(iloop=0;iloop<19;iloop++)     // for loop to get the string char by char

Второй недостаток заключается в том, что вы не проверяете успешность вызова scanf(). Если вы обнаружите сбой, вы также должны выйти из цикла:

        if (scanf("%c",&cvalue[iloop]) != 1) { //getting input
            break;
        }

Ниже была моя попытка создать более безопасную версию gets(), реализованную с помощью scanf().

char *getsn (char *s, size_t sz) {
    char c;
    char fmt[sizeof(sz) * CHAR_BIT + sizeof("[^\n]")];
    if (sz == 0) return 0;
    if (sz == 1) {
        s[0] = '\0';
        return s;
    }
    s[sz-2] = '\0';
    snprintf(fmt, sizeof(fmt), "%%%lu%s", (unsigned long)sz-1, "[^\n]");
    switch (scanf(fmt, s)) {
    case 0:  s[0] = '\0';
             scanf("%c", &c);
             return s;
    case 1:  scanf("%c", &c);
             if (s[sz-2] != '\0' && c != '\n') {
                 ungetc(c, stdin);
             }
             return s;
    default: break;
    }
    return 0;
}

Более безопасная версия использует snprintf() для создания строки формата, ограничивающей количество символов, которое должно храниться в scanf(). Таким образом, если предоставленный параметр sz равен 100, результирующая строка формата будет "%99[^\n]". Затем он удаляет \n из входного потока только в том случае, если он действительно встречается.

person jxh    schedule 14.06.2013
comment
Новое использование printf() для создания максимальной ширины поля для scanf(fmt, s). - person chux - Reinstate Monica; 18.06.2013