2016-02-07 2 views
5

Я видел несколько примеров, где люди дают scanf строку "%[^\n]\n", чтобы прочитать целую строку ввода пользователя. Если мое понимание верное, это будет читать каждый символ до тех пор, пока не будет достигнут символ новой строки, а затем новая строка будет использована scanf (и не будет включена в результирующий ввод).Почему эта строка scanf не работает? "% [^ n] n"

Но я не могу заставить это работать на моей машине. Простой пример, который я пробовал:

#include <stdio.h> 

int main(void) 
{ 
    char input[64]; 

    printf("Enter some input: "); 
    scanf("%[^\n]\n", input); 

    printf("You entered %s\n", input); 
} 

Когда я запускаю это, я пробужден для ввода, я набираю несколько символов, я ударил Enter, и курсор переходит к началу следующей строки, но зсап вызов не завершается.

scanf not finished

я могу ударить Enter столько раз, сколько мне нравится, и это никогда не закончится.

scanf still not finished

единственными способами, которые я нашел, чтобы завершить вызов Scanf являются:

  • введите \n как первый (и единственный) символ в командной строке
  • введите Ctrl-d, как первый (и единственный) символ в строке
  • введите один вход, один или несколько \n, ноль или несколько других символов, и введите Ctrl-d

Я не знаю, зависит ли это от машины, но мне очень любопытно узнать, что происходит. Я на OS X, если это актуально.

+3

Не размещайте изображения текста! – Olaf

+1

его% [^ \ n], попробуйте это –

+0

@ user3121023 Спасибо, именно поэтому я хотел использовать ''% [^ \ n] \ n ". – ivan

ответ

5

Согласно documentation для зсапЕ (акцент мой):

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

Таким образом, ваш формат строки %[^\n]\n будет первым читать (и магазин) произвольное количество не пробельных символов из входного (из-за %[^\n] части), а затем, из-за следующей строки, читать (и отбросить) произвольное количество пробельных символов, таких как пробелы, вкладки или символы новой строки.

Таким образом, чтобы сделать ваш зсапЕ остановки чтения ввода, вам необходимо либо ввести по крайней мере один непробельный характер после того, как символ новой строки, или же организовать входной поток до конца (например, нажав Ctrl + D в системах Unix-ish).

Вместо этого, чтобы ваш код работал так, как вы ожидаете, просто удалите последний \n с конца строки вашего формата (как уже было предложено Umamahesh P).

Конечно, это оставит новую строку еще во входном потоке. Чтобы избавиться от него (в случае, если вы хотите прочитать другую строку позже), вы можете отключить его от потока или просто добавить %*c (что означает «прочитать один символ и отбросить его») или даже %*1[\n] (прочитайте одну новую строку и отбросьте это) до конца строки формата scanf.

Ps. Обратите внимание, что в вашем коде есть еще несколько проблем. Например, чтобы избежать ошибок переполнения буфера, вы действительно должны использовать %63[^\n] вместо %[^\n], чтобы ограничить количество символов scanf, которые будут прочитаны в вашем буфере. (Предел должен быть на один меньше размера вашего буфера, так как scanf всегда будет добавлять конечный нулевой символ.)

Кроме того, спецификатор формата %[ всегда ожидает по крайней мере одного совпадающего символа и не будет работать, если ни один из них не будет доступный. Таким образом, если вы сразу нажмете Enter, не набрав ничего, ваш scanf завершится неудачно (тихо, так как вы не проверите возвращаемое значение) и оставит буфер ввода заполненным случайным мусором. Чтобы этого избежать, вы должны a) проверить возвращаемое значение scanf, b) установить input[0] = '\0' перед вызовом scanf или c) предпочтительно оба.

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

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

void chomp(char *string) { 
    int len = strlen(string); 
    if (len > 0 && string[len-1] == '\n') string[len-1] = '\0'; 
} 

int main(void) 
{ 
    char input[64]; 

    printf("Enter some input: "); 
    fgets(input, sizeof(input), stdin); 
    chomp(input); 

    printf("You entered \"%s\".\n", input); 
} 
+1

Ваш ответ отлично справляется с объяснением того, что происходит и как правильно его обрабатывать. Я использую scanf, потому что это связано с упражнением в книге, над которой я работаю, и я опустил некоторые детали, чтобы сделать вопрос более ясным, но ваши замечания о fgets и переполнении буфера отмечены должным образом. Благодаря! – ivan

1

Извлеките второй символ новой строки и достаточно.

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

Чтобы ответить на оригинал,

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

Это также должно работать, если вы вводите непробельным символ после ввода. Пример:

Enter some input: lkfjdlfkjdlfjdlfjldj 
t 
You entered lkfjdlfkjdlfjdlfjldj 
+1

Этот ответ был бы значительно улучшен объяснением * почему * этого достаточно, и почему разумный синтаксис OP не работает. –

+0

Изменен ответ, чтобы предоставить более подробную информацию. Спасибо. –

+0

В вашем примере должен быть «\\ n» вместо «\ n». –

3

Пробелов символов в format из scanf() имеет особое значение:

Пробела характера: функция будет читать и игнорировать любые пробела символов, встречающихся до следующего непробельного символа (пробельного символы включают пробелы, символы новой строки и табуляции - см. isspace). Единственное пробельное выражение в строке формата проверяет любое количество пробельных символов, выделенных из потока (в том числе нет).

Таким образом, "%[^\n]\n" просто эквивалентно "%[^\n] ", говоря scanf() игнорировать все пробельные символы после %[^\n]. Вот почему все '\n' s игнорируются до тех пор, пока не будет введен символ без пробелов, что произошло в вашем случае.

Ссылка: http://www.cplusplus.com/reference/cstdio/scanf/

Смежные вопросы