Вопрос по c, whitespace, string, printf, scanf – Как разрешить ввод пробелов с помощью scanf?

117

Используя следующий код:

char *name = malloc(sizeof(char) + 256); 

printf("What is your name? ");
scanf("%s", name);

printf("Hello %s. Nice to meet you.\n", name);

Пользователь может ввести свое имя, но когда он вводит имя с пробелом, как,Lucas Aardvarkscanf() просто обрезает все послеLucas, Как я могу сделатьscanf() разрешить пробелы

@ Барри - я подозреваюsizeof(char) + 256 была опечатка. Chris Lutz
Обратите внимание, что более идиоматическийmalloc (sizeof (char) * 256 + 1) ', или же 'malloc (256 + 1) 'или даже лучше (при условииназвание' будет использоваться строго локально)имя символа [256 + 1] ', '+1' может действовать как мнемоника для нулевого терминатора, который должен быть включен в распределение. Barry Kelly

Ваш Ответ

11   ответов
4

getline()

Теперь часть POSIX, тем не менее.

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

Стандартный? В ссылке вы цитируете:Оба getline () и getdelim () являются расширениями GNU. " AProgrammer
POSIX 2008 добавляет getline. Таким образом, GNU пошел вперед и изменил свои заголовки для glibc в версии 2.9, и это создает проблемы для многих проектов. Не окончательная ссылка, но посмотрите здесь:bugzilla.redhat.com/show_bug.cgi?id=493941 , Что касается онлайновой справочной страницы, я взял первую найденную Google. dmckee
45

В этом примере используется инвертированный набор сканирования, поэтому scanf продолжает принимать значения, пока не встретит ''- перевод строки, поэтому пробелы также сохраняются

#include 

int main (int argc, char const *argv[])
{
    char name[20];
    scanf("%[^\n]s",name);
    printf("%s\n", name);
    return 0;
}
Осторожнее с переполнением буфера. Если пользователь пишет "название" с 50 символами, программа, вероятно, вылетит. brunoais
Как вы знаете размер буфера, вы можете использовать%20[^\n]s предотвратить переполнение буфера osvein
164

Люди (иособенно начинающие) никогда не должны использоватьscanf("%s") или жеgets() или любые другие функции, которые не имеют защиты от переполнения буфера, если вы не уверены наверняка, что входные данные всегда будут иметь определенный формат (и, возможно, даже тогда).

Помните чемscanf обозначает "Сканирование в формате " и там'драгоценный маленькийМеньше отформатирован, чем введенные пользователем данные. Это'Идеально, если у вас есть полный контроль над форматом входных данных, но, как правило, не подходит для ввода пользователем.

использованиеfgets() (которыйимеет защита от переполнения буфера), чтобы получить ваш ввод в строку иsscanf() оценить это. Так как вы просто хотите, что пользователь ввел без разбора, вы неэто действительно нужноsscanf() в любом случае в этом случае:

#include 
#include 
#include 

/* Maximum name size + 1. */

#define MAX_NAME_SZ 256

int main(int argC, char *argV[]) {
    /* Allocate memory and check if okay. */

    char *name = malloc(MAX_NAME_SZ);
    if (name == NULL) {
        printf("No memory\n");
        return 1;
    }

    /* Ask user for name. */

    printf("What is your name? ");

    /* Get the name, with size limit. */

    fgets(name, MAX_NAME_SZ, stdin);

    /* Remove trailing newline, if there. */

    if ((strlen(name) > 0) && (name[strlen (name) - 1] == '\n'))
        name[strlen (name) - 1] = '\0';

    /* Say hello. */

    printf("Hello %s. Nice to meet you.\n", name);

    /* Free memory and exit. */

    free (name);
    return 0;
}
Если вы просто хотите получить строку от пользователя, это проще. Это'Это также безопаснее, поскольку вы можете избежать переполнения буфера. Семейство scanf действительно полезно для преобразования строки в разные вещи (например, четыре символа и int, например, с помощью "% C% C% C% C% d») но даже тогда вы должны использовать fgets и sscanf, а не scanf, чтобы избежать возможности переполнения буфера. paxdiablo
Скажи это моему профессору. Jonathan Komar
Вы можете указать максимальный размер буфера в формате scanf, простоположить вычисленный во время выполнения без построения формата во время выполнения (нетt эквивалент * для printf, * является допустимым модификатором для scanf с другим поведением: подавление присваивания). AProgrammer
Я не'не знаю оfgets(), Это на самом деле выглядит проще в использованииscanf(), +1 Kredns
114

Пытаться

char str[11];
scanf("%10[0-9a-zA-Z ]", str);

Надеюсь, это поможет.

обратите внимание, это нене делаю общих регулярных выражений, просто классы символов rampion
Я не'даже не знаюscanf() принято регулярное выражениеs! Спасибо!!!!! Kredns
буду идти с"%[^\n]" как строка формата " Christoph
(1) Очевидно, чтобы принимать пробелы, вам нужно поставить пробел в классе символов. (2) Обратите внимание, что 10 - это максимальное количество символов, которое будет прочитано, поэтому str должен указывать как минимум на буфер размером 11. (3) последние сt директива формата, но scanf попытается здесь точно соответствовать. Эффект будет виден на записи типа 1234567890s, где s будет потребляться, но не указывать, где. Другое письмо выигралоТ быть потребленным. Если после s поставить другой формат, он будет доступен только для чтения, если есть s для сопоставления. AProgrammer
6

Вы можете использоватьfgets() функция для чтения строки или использованияscanf("%[^\n]s",name); поэтому чтение строки будет прекращено при обнаружении символа новой строки.

Осторожно, что это не предотвращает переполнение буфера brunoais
21

Вы можете использовать это

char name[20];
scanf("%20[^\n]", name);

Или это

void getText(char *message, char *variable, int size){
    printf("\n %s: ", message);
    fgets(variable, sizeof(char) * size, stdin);
    sscanf(variable, "%[^\n]", variable);
}

char name[20];
getText("Your name", name, 20);

DEMO

Как примечание,sizeof(char) по определениювсегда 1, так чтоНет необходимости умножать на это. paxdiablo
Я не тестировал, но, основываясь на других ответах на этой самой странице, я считаю, что правильный размер буфера для scanf в вашем примере:scanf("%19[^\n]", name); (еще +1 за краткий ответ) Dr Beco
-3

Используйте следующий код для чтения строки с пробелами:

scanf("[%s/n]",str_ptr);

4

Если кто-то все еще ищет, здесьЧто мне помогло - прочитать произвольную длину строки, включая пробелы.

Спасибо многим постерам в сети за то, что они поделились этим простым & элегантное решение. Если это работает, кредит идет к ним, но любые ошибки - мои.

char *name;
scanf ("%m[^\n]s",&name);
printf ("%s\n",name);
Это'Стоит отметить, что этоPOSIX расширение и не существует в стандарте ISO. Для полноты вы, вероятно, должны также проверятьerrno и очистка выделенной памяти, а также. paxdiablo
1

Вы можете использоватьscanf для этого с небольшим трюком. На самом деле, вы должны разрешить ввод данных пользователем, пока пользователь не нажмет Enter (\n). Это будет учитывать каждый персонаж, в том числепространство, Вот пример:

int main()
{
  char string[100], c;
  int i;
  printf("Enter the string: ");
  scanf("%s", string);
  i = strlen(string);      // length of user input till first space
  do
  {
    scanf("%c", &c);
    string[i++] = c;       // reading characters after first space (including it)
  } while (c != '\n');     // until user hits Enter
  string[i - 1] = 0;       // string terminating
return 0;
}

Как это работает? Когда пользователь вводит символы из стандартного ввода, они будут сохранены встрока переменная до первого пробела. После этого оставшаяся часть записи останется во входном потоке и будет ждать следующего сканирования. Далее у нас естьfor цикл, который принимает символ за символом из входного потока (до\n) и добавляет их в конецстрока переменная, таким образом формируя полную строку, такую же как пользовательский ввод с клавиатуры.

Надеюсь, это кому-нибудь поможет!

Подлежит переполнению буфера. paxdiablo
7

Дон»т использоватьscanf() читать строки без указания ширины поля. Вы также должны проверить возвращаемые значения на наличие ошибок:

#include 

#define NAME_MAX    80
#define NAME_MAX_S "80"

int main(void)
{
    static char name[NAME_MAX + 1]; // + 1 because of null
    if(scanf("%" NAME_MAX_S "[^\n]", name) != 1)
    {
        fputs("io error or premature end of line\n", stderr);
        return 1;
    }

    printf("Hello %s. Nice to meet you.\n", name);
}

В качестве альтернативы используйте:fgets()

#include 

#define NAME_MAX 80

int main(void)
{
    static char name[NAME_MAX + 2]; // + 2 because of newline and null
    if(!fgets(name, sizeof(name), stdin))
    {
        fputs("io error\n", stderr);
        return 1;
    }

    // don't print newline
    printf("Hello %.*s. Nice to meet you.\n", strlen(name) - 1, name);
}
0

Хотя ты действительно не должент использоватьscanf() для такого рода вещей, потому что есть гораздо лучшие звонки, такие какgets() или жеgetline(), это может быть сделано:

#include 

char* scan_line(char* buffer, int buffer_size);

char* scan_line(char* buffer, int buffer_size) {
   char* p = buffer;
   int count = 0;
   do {
       char c;
       scanf("%c", &c); // scan a single character
       // break on end of line, string terminating NUL, or end of file
       if (c == '\r' || c == '\n' || c == 0 || c == EOF) {
           *p = 0;
           break;
       }
       *p++ = c; // add the valid character into the buffer
   } while (count < buffer_size - 1);  // don't overrun the buffer
   // ensure the string is null terminated
   buffer[buffer_size - 1] = 0;
   return buffer;
}

#define MAX_SCAN_LENGTH 1024

int main()
{
   char s[MAX_SCAN_LENGTH];
   printf("Enter a string: ");
   scan_line(s, MAX_SCAN_LENGTH);
   printf("got: \"%s\"\n\n", s);
   return 0;
}
Там'спричина Зачемgets устарела и удалена (stackoverflow.com/questions/30890696/why-gets-is-deprecated) из стандарта. Это'Семьхуже тотscanf потому что по крайней мере у последнего есть способы сделать это безопасным. paxdiablo

Похожие вопросы