2013-02-27 3 views
1

Я смущен о поведении scanf при обработке пробелов.scanf buffering с вкладками и новыми линиями

Вот код, я использую:

int main() 
{ 
    int val; 
    int c; 

    scanf("%d\t", &val); 

    c = getchar(); 

    if(c == EOF) 
    printf("eof"); 
    else if(c == '\t') 
    printf("tab"); 
    else if(c == '\n') 
    printf("newline"); 
} 

А вот вход, который я передаю его:

1234<tab><newline> 

Я ожидал бы это напечатать «перевод строки», так как зсапЕ ищет только вкладки, и предположительно scanf оставляет пустое пространство в буфере по умолчанию. Вместо этого он печатает «eof». Кажется, что %d\t проглотит новую линию.

Я пропустил что-то о том, как работает scanf?

Обратите внимание, что, если изменить его к следующему, он правильно печатает «строку»:

int main() 
{ 
    int val; 
    int c; 

    scanf("%d", &val); 

    getchar(); /* note this extra getchar */ 

    c = getchar(); 

    if(c == EOF) 
    printf("eof"); 
    else if(c == '\t') 
    printf("tab"); 
    else if(c == '\n') 
    printf("newline"); 
} 

ответ

3

Любое количество пробелов в шаблоне (\t) соответствует любому количества пробелов на входе (\t\n).

От человека странице:

White space (such as blanks, tabs, or newlines) in the format string match any amount of white space, including none, in the input. 
0

«Я запутался о поведении зсапЕ при обработке пробелов.» является универсальным требованием!

Если в строке формата есть пробелы, он будет использовать все пробелы, поэтому «\ t» соответствует любой строке пробелов.

1

Вы столкнулись с одной из наиболее печально известных причин, почему *scanf should never be used: запутанная обработка пробелов. Ваш '\t' не просто соответствует одной вкладке, он соответствует любого количества пробелов, включая новую строку!

Лучший способ сделать такого рода вещи, если у вас есть getline, выглядит следующим образом:

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

int main(void) 
{ 
    char *line = 0; 
    char *p; 
    long val; 

    (void) getline(&line, 0, stdin); 

    val = strtol(line, &p, 10); 
    if (*p == '\0') 
     puts("eof (no tab)"); 
    else { 
     if (*p != '\t') 
      printf("no tab, %c instead\n", *p); 
     p++; 
     if (*p == '\0') 
      puts("eof"); 
     else if (*p == '\t') 
      puts("tab"); 
     else if (*p == '\n') 
      puts("newline"); 
    } 

    free(line); 
    return 0; 
} 

Если у вас нет getline, fgets часто достаточно хорошо. (ПРЕДУПРЕЖДЕНИЕ: не путайте fgets с gets. gets опасен и никогда не должен использоваться, но fgets является неудобным, если вы хотите, чтобы ваша программа была надежной в случае чрезмерно длинных линий ввода.)

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