Вопрос по c, printf, string, scanf, whitespace – Как разрешить ввод пробелов с помощью 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 Aardvark, scanf() просто обрезает все послеLucas, Как я могу сделатьscanf() разрешить пробелы

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

Ваш Ответ

11   ответов
6

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

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


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

45

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

#include <stdio.h>

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

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

#include <stdio.h>

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;
}
Там естьreason Зачемgets устарела и удалена (stackoverflow.com/questions/30890696/why-gets-is-deprecated) из стандарта. Это дажеworse тотscanf потому что по крайней мере у последнего есть способы сделать это безопасным.
164

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

Помните чемscanf расшифровывается как «отформатированный скан» и там очень малоless отформатирован, чем введенные пользователем данные. Это идеально, если у вас есть полный контроль над форматом входных данных, но, как правило, не подходит для ввода пользователем.

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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* 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, вы просто не можете поместить вычисленный во время выполнения, не создавая формат во время выполнения (нет эквивалента * для printf, * является допустимым модификатором для scanf с другим поведением: подавление присваивания ).
Я не знал оfgets(), Это на самом деле выглядит проще в использованииscanf(). +1 Kredns
Скажи это моему профессору.
Если вы просто хотите получить строку от пользователя, это проще. Это также более безопасно, поскольку вы можете избежать переполнения буфера. Семейство scanf действительно полезно для преобразования строки в разные вещи (например, четыре символа и целое число, например, с "% c% c% c% c% d"), но даже тогда вы должны использовать fgets и sscanf, не scanf, чтобы избежать возможности переполнения буфера.
1

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

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

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

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

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

Пытаться

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

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

@rampion: Отличная заметка (но все же это круто). Kredns
обратите внимание, что он не выполняет общие регулярные выражения, только классы символов
Я даже не зналscanf() принятые регулярные выражения! Спасибо!!!!! Kredns
почему трейлингs в строке формата?
(1) Очевидно, чтобы принимать пробелы, вам нужно поставить пробел в классе символов. (2) Обратите внимание, что 10 - это максимальное количество символов, которое будет прочитано, поэтому str должен указывать как минимум на буфер размером 11. (3) Конечные s здесь не являются директивой формата, но scanf попытается здесь точно соответствовать ей. Эффект будет виден на записи типа 1234567890s, где s будет потребляться, но не указывать, где. Другое письмо не будет израсходовано. Если после s поставить другой формат, он будет доступен только для чтения, если есть s для сопоставления.
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

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

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

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

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

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

#include <stdio.h>

#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 <stdio.h>

#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);
}
4

getline()

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

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

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

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