Введите Quit для выхода из программы C - PullRequest
2 голосов
/ 07 марта 2019

Мне было интересно, в чем проблема с моей программой.Я не могу заставить программу выйти, когда набираю quit.Вот что у меня есть:

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

int main(void) {

    char string[200];

    printf("Enter a bunch of words: ");
    do
    {
        scanf("%[^\n]c", string);


    }while(strcmp(string,"quit")!=0);

    return 0;
}

Ответы [ 3 ]

4 голосов
/ 07 марта 2019

Две ваши самые большие проблемы - две наиболее распространенные проблемы, которые преследуют новых программистов на С с использованием scanf:

  1. Вы используете неверную строку формата;и
  2. Вы не можете проверить возвращение scanf.

Давайте сначала разберемся с первым:

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

Строка формата "%[^\n]c" использует символьный класс спецификатор формата "%[...]", чтобы прочитать текст для string.Затем следует "c" - который будет совпадать только с литералом 'c' в конце вашей входной строки.Это не может произойти, как написано, потому что "%[^\n]" будет читать все символы, которые не являются '\n', оставляя только '\n' для чтения - , который НЕ соответствует 'c'.

Кроме того, спецификатор "%[...]" вместе со спецификатором "%c" НЕ занимают начальные пробелы ('\n' - пробелы).Таким образом, оставив '\n' непрочитанным в stdin, ваш следующий вызов на scanf не удастся, потому что "%[^\n]" не будет читать '\n' и не будет совпадать с 'c', что приведет к сбою совпадения , '\n' остается непрочитанным в stdin, и все быстро выходит из-под контроля.

Чтобы решить все проблемы, вам нужно запомнить (2.) выше, а также использовать field-widthМодификатор для защиты границ массива string, после чего вы должны прочитать и Сохранить символ, следующий за извлеченными, и вставить от string до validate была прочитана полная строка ввода - и если нет, то ваша ответственность удалить все лишние символы, которые остаются в stdin до попытка следующего чтения.

Для начала вы можете использовать правильно ограниченную строку формата , которая включает space в начале, что приведет к отбрасыванию scanfвсе пробелы, например

    " %199[^\n]%c"

Примечание над финаломсимвол будет сохранен, два преобразования будут иметь место, поэтому вам понадобится символьная переменная для обработки результата окончательного спецификатора преобразования, например,

    do {
        char c;     /* final character read */
        int retn;   /* variable to save scanf return */
        /* prompt */
        fputs ("Enter a bunch of words ('quit' exits): ", stdout);
        /* read saving scanf return */
        retn = scanf (" %199[^\n]%c", string, &c);

( примечание: приглашение было перемещено в цикле do {...} while (..);)

Далее вы ответственны за проверку возврат из scanf каждый раз .Вы должны обработать три условия

  1. (return == EOF) пользователь отменяет ввод, генерируя руководство EOF, нажимая Ctrl + d (или в Windows Ctrl + z );
  2. (return < expected No. of conversions), вы должны обработать соответствующий или input , и вы должны учитыватьдля каждого символа, который может остаться в вашем входном буфере.(обычно вы будете сканировать вперед во входном буфере до тех пор, пока не будет найден '\n' или EOF, отбрасывающий все оставшиеся посторонние символы, см. функцию empty_stdin() в примере);и
  3. (return == expected No. of conversions), указывающий на успешное чтение - тогда вам нужно проверить, соответствует ли входные данные каким-либо дополнительным критериям (например, положительное целое число, положительная плавающая точка и т. д.).

В целом, вы можете обрабатывать чтение циклов с помощью scanf и искать "quit" в качестве ключевого слова, подсказывающего выход, следующим образом:

    do {
        char c;     /* final character read */
        int retn;   /* variable to save scanf return */
        /* prompt */
        fputs ("Enter a bunch of words ('quit' exits): ", stdout);
        /* read saving scanf return */
        retn = scanf (" %199[^\n]%c", string, &c);
        if (retn == EOF) {      /* check the return against EOF */
            fputs ("(user canceled input)\n", stderr);
            return 0;
        }
        else if (retn < 2) {    /* checking both string and c read */
            fputs ("input failure.\n", stderr);
            empty_stdin();
        }
        else if (c != '\n') {   /* check c is '\n', else string too long */
            fprintf (stderr, "warning: input exceeds %d characters.\n",
                    MAXC - 1);
            empty_stdin();
        }
        else    /* good input, output string */
            printf ("string: %s\n", string);

    } while (strcmp (string,"quit") != 0);

Наконец, НЕ используйте magic-номер в вашем коде (200 это магический номер ).Вместо этого, если вам нужна константа , #define один (или больше).Единственное место, где вы должны жестко кодировать числа, это, например, модификатор scanf field-width - который не может использовать переменную, макрос или именованную константу.Это одно исключение из правил.Аналогично, НЕ жестко кодируйте имена файлов или пути.Все функции принимают аргументы, даже main(), передают необходимую информацию в вашу программу.

В целом, вы можете сделать что-то вроде:

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

#define MAXC 200    /* constant - maximum characters in string */ 

void empty_stdin (void)
{
    int c = getchar();
    while (c != EOF && c != '\n')
        c = getchar();
}

int main (void) {

    char string[MAXC];    /* use constants for array bounds */

    do {
        char c;     /* final character read */
        int retn;   /* variable to save scanf return */
        /* prompt */
        fputs ("Enter a bunch of words ('quit' exits): ", stdout);
        /* read saving scanf return */
        retn = scanf (" %199[^\n]%c", string, &c);
        if (retn == EOF) {      /* check the return against EOF */
            fputs ("(user canceled input)\n", stderr);
            return 0;
        }
        else if (retn < 2) {    /* checking both string and c read */
            fputs ("input failure.\n", stderr);
            empty_stdin();
        }
        else if (c != '\n') {   /* check c is '\n', else string too long */
            fprintf (stderr, "warning: input exceeds %d characters.\n",
                    MAXC - 1);
            empty_stdin();
        }
        else    /* good input, output string */
            printf ("string: %s\n", string);

    } while (strcmp (string,"quit") != 0);

    return 0;
}

Пример Использование / Вывод

$ ./bin/scanf_string_quit
Enter a bunch of words ('quit' exits): Hello
string: Hello
Enter a bunch of words ('quit' exits): My dog has fleas and my cat has none.
string: My dog has fleas and my cat has none.
Enter a bunch of words ('quit' exits): quit
string: quit

Создание руководства EOF с помощью Ctrl + d (или Ctrl + z при повороте):

$ ./bin/scanf_string_quit
Enter a bunch of words ('quit' exits): Hello
string: Hello
Enter a bunch of words ('quit' exits): (user canceled input)

Сброс MAXC до 20 и ширина поля модификатор до scanf до 19 вы можете проверить обработку слишком длинных строк, например, первый вход подходит, второйслишком длинный:

$ ./bin/scanf_string_quit
Enter a bunch of words ('quit' exits): my dog has fleas and my cat has none.
warning: input exceeds 19 characters.
Enter a bunch of words ('quit' exits): 1234567890123456789
string: 1234567890123456789
Enter a bunch of words ('quit' exits): 12345678901234567890
warning: input exceeds 19 characters.
Enter a bunch of words ('quit' exits): quit
string: quit

Посмотрите, что произошло, и дайте мне знать, если у вас есть дополнительные вопросы.

1 голос
/ 07 марта 2019

Допустимо ли что-то подобное?

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

int main(void) {

    char string[200] = {0};

    printf("Enter a bunch of words: ");
    do {
        memset(string, 0, 200);
        scanf("%s", string);
    } while (strcmp(string, "quit") != 0);

    return 0;
}

Вы точно не объяснили, что вы собираетесь делать со строкой, поэтому сложно дать ответ. Однако следует отметить, что вы должны либо сделать что-то для string (я только что обнулел это здесь), чтобы strcmp распознал "выход", либо сканировать подстроки string, потому что если все всегда добавляется, тогда ваша строка будет "(...) quit", которую strcmp не распознает как "quit".

Как примечание, всегда инициализируйте массивы, иначе могут произойти плохие вещи.

0 голосов
/ 07 марта 2019

Учитывая предоставленное вами скудное объяснение, наиболее простым изменением может быть изменение строки сканирования для проглатывания \n вместо теоретического c, что на самом деле невозможно:

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

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

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

Это безопасно, если вы знаете, что ввод никогда не превысит 199 символов. Но это слабое предположение как в теории, так и на практике. Символы после 199 сохраненных будут отсканированы как ваше следующее слово на следующей итерации, что позволит вашей программе неожиданно завершить работу, если введено значение 199 . с, за которым следует слово quit. Вам нужен более надежный способ отсканировать до конца строки и отбросить ее перед прочтением следующей строки.

Возможно, у вас возникнет соблазн использовать другой %[...] для захвата дополнительных символов, таких как:

        scanf("%199[^\n]%*[^\n]\n", string);

Однако это не удастся для общего случая, когда ввод меньше или равен 199 символам. Это потому, что scanf не удастся, если преобразование приведет к пустому вводу. Таким образом, \n будет оставаться замятым на входе.

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

        scanf("%199[^\n]%*[^\n]", string);
        scanf("\n");
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...