2017-02-16 13 views
0

По какой-то причине scanf читает символ, но программа не продолжает цикл while, используемый для проверки символа, даже если символ действителен. Иногда это работает, а иногда - нет. Странно то, что я не делаю ничего другого. Есть идеи?Символ Scanf в C

do 
{ 
    printf("Enter \"p\" if you want to sort and shuffle a list of players or enter \"s\" if you want to sort a list of slots: "); // prompt 
    scanf("%c", &tmp); 
} while (tmp != 'p' && tmp != 's'); 

новый код:

printf("Enter 'p' if you want to sort and shuffle a list of players or enter 's' if you want to sort a list of slots:"); // prompt 
tmp = getchar(); 

printf("%c ",tmp); 
if (tmp == 'p') 
{ 
    size = readPlayerFile(players); // calling readPlayerFile function 
    shufflePlayers(players, size); // call shufflePlayers function 
    sortPlayers(players, size); // call sortPlayers function 
} 
else if (tmp == 's') 
{ 
    printf("hello"); 
    size = readSlotFile(slots); // calling readSlotFile function 
    sortSlot(slots, size); // call sortSlots function 

} 
+2

Проводили ли вы базовую отладку? Как использовать отладчик и/или отладочные операторы печати, чтобы узнать, какие значения принимает 'tmp', когда он работает, а когда нет? – kaylum

+0

Он отлично работает в моей системе. – Jarvis

+1

Что вы читали перед этим? Оставило ли он новую строку на входе? Вы должны рассмотреть возможность использования 'if (scanf ("% c ", & tmp)! = 1) {... handle error ...}' где пробел перед '% c' не является случайностью - он пропускает пробел (пробелы, вкладки, newlines) и ждет чего-то другого ('' '' '' '' '' '' '), который будет введен. –

ответ

1

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

#include <stdio.h> 

int main(void) 
{ 

    char tmp; 
    printf("Enter \"p\" if you want to sort and shuffle a list of players " 
      "or enter \"s\" if you want to sort a list of slots: "); // prompt 
    while (scanf(" %c", &tmp) != 1 || (tmp != 'p' && tmp != 's')) { 
     printf("Please enter \"p\" or \"s\": "); 
    } 
    printf("You chose %c\n", tmp); 

    printf("Choose \"a\"scending or \"d\"escending sort: "); 
    while (scanf(" %c", &tmp) != 1 || (tmp != 'a' && tmp != 'd')) { 
     printf("Please enter \"a\" or \"d\": "); 
    } 
    printf("You chose %c\n", tmp); 

    return 0; 
} 

Существует проблема с этим простым подходом, хотя. Если пользователь вводит "aaap" то полученный результат будет:

Enter "p" if you want to sort and shuffle a list of players or enter "s" if you want to sort a list of slots: aaap 
Please enter "p" or "s": Please enter "p" or "s": Please enter "p" or "s": You chose p 
Choose "a"scending or "d"escending sort: 

Или, что еще хуже, если пользователь вводит правильный первый символ следуют другие символы, такие как "sfffa":

Enter "p" if you want to sort and shuffle a list of players or enter "s" if you want to sort a list of slots: sfffa 
You chose s 
Choose "a"scending or "d"escending sort: Please enter "a" or "d": Please enter "a" or "d": Please enter "a" or "d": You chose a 

Потому что scanf() листья непревзойденные символы во входном потоке, эти символы должны быть обработаны перед чтением входного потока снова. В простом решении, использующем " %c", начальное пространство заставляет scanf() пропускать ведущие символы пробела, но любые другие символы будут подняты. И в реальном мире вы не можете рассчитывать на взаимодействие пользователя, предоставляя корректный ввод.

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

#include <stdio.h> 

... 

char tmp; 
int c; 
int scanf_ret; 

do { 
    printf("Enter \"p\" if you want to sort and shuffle a list of players " 
      "or enter \"s\" if you want to sort a list of slots: "); // prompt 
    scanf_ret = scanf("%c", &tmp); 
    while ((c = getchar()) != '\n' && c != EOF) { 
     continue;     // discard extra characters 
    } 
} while (scanf_ret != 1 || (tmp != 'p' && tmp != 's')); 

После вызова scanf(), петля вводится, который считывает и отбрасывает любые символы, оставшиеся во входном потоке. Обратите внимание, что c является int и что EOF проверяется явно в цикле. Функция getchar() может возвращать EOF в случае ошибки или если пользователь сигнализирует EOF с клавиатуры или если вход был перенаправлен из файла. Невозможность проверки на EOF в таких обстоятельствах приведет к бесконечному циклу.

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

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

... 

char buffer[1000]; 
char tmp; 
int sscanf_ret; 

do { 
    printf("Enter \"p\" if you want to sort and shuffle a list of players " 
      "or enter \"s\" if you want to sort a list of slots: "); // prompt 
    if (fgets(buffer, sizeof buffer, stdin) == NULL) { 
     fprintf(stderr, "Error in fgets()\n"); 
     exit(EXIT_FAILURE); 
    } 
    sscanf_ret = sscanf(buffer, "%c", &tmp); 
} while (sscanf_ret != 1 || (tmp != 'p' && tmp != 's')); 

fgets() функция получает целую строку ввода, через символ новой строку, или получает (sizeof buffer) - 1 символов из входного потока, если буфер не является достаточно большим, чтобы вместить все символы во входном потоке и \0 терминатора , Предоставляя буфер большого размера, это должно работать нормально. Пользователь по-прежнему может вводить очень большое количество символов, и, хотя это не будет переполнять буфер, символы будут оставлены во входном потоке для следующей операции чтения. Чтобы избежать таких проблем, , если остались лишние символы, входной поток должен быть очищен после вызова fgets() с использованием петли getchar() сверху.

Все эти методы имеют свое место. Очевидно, что последние два гораздо более надежны, чем первые, и, поскольку маловероятно, что пользователь вводит 999 символов в приглашении ввода (включая новую строку), они почти равны. Но, во избежание всякого удивления, явное очищение входного потока после получения пользовательского ввода является лучшим.

+0

спасибо за длинное объяснение, я этого не знал! ive избавился от валидатора, потому что у меня все еще были проблемы, поэтому я просто получил приглашение и tmp = getchar(); и по какой-то причине он все еще не работает, я пытаюсь напечатать характер сразу после его чтения, и он все еще не работает. – james

+0

Если вы этого еще не сделали, вам может потребоваться распечатать новую строку, чтобы очистить выходной буфер до экрана: 'printf ("% c \ n ", tmp);'. Если вы добавите раздел «Редактировать» в конец вашего вопроса с новым кодом, я посмотрю. –

+0

спасибо, я попробую, теперь я отредактировал вопрос с новым кодом, я пытаюсь упростить его, чтобы попытаться найти проблему. странно, что он работает с числами, читающими их как символы, но не с p или s – james

0

Во-первых, scanf() выходит во входном буфере в \n, и он остается в буфере ввода в следующий раз, он называется. Вам нужно добавить пространство для вашего спецификатора формата:

scanf(" %c", &tmp) 

Чтобы пропустить пробельных символов, которые могут быть символы новой строки, пробелы или вкладок.

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

Ваш код может выглядеть следующим образом:

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

int main(void) { 
    char temp; 

    do { 
     printf("Enter \"p\" if you want to sort and shuffle a list of players or enter \"s\" if you want to sort a list of slots: "); // prompt 
     if (scanf(" %c", &tmp) != 1) { 
      printf("Invalid character\n"); 
      exit(EXIT_FAILURE); 
     } 
    } while (tmp != 'p' && tmp != 's'); 

    return 0; 
}