если моя переменная scanf - это число с плавающей точкой, а пользователь вводит символ, как я могу предложить ему ввести число?при условии, что scanf находится внутри цикла do while - PullRequest
0 голосов
/ 25 января 2019

Я пытался использовать k = getchar (), но он тоже не работает;

вот мой код

#include<stdio.h>
int main()
{
    float height;
    float k=0;
    do 
    {
        printf("please type a value..\n");
        scanf("%f",&height);
        k=height;
    }while(k<0);// i assume letters and non positive numbers are below zero.
    //so i want the loop to continue until one types a +ve float.
    printf("%f",k);



    return 0;
}

Я хочу, если пользователь вводит буквы или отрицательныецифры или символы, которые он / она должен будет ввести снова, пока он не введет положительное число

Ответы [ 2 ]

0 голосов
/ 25 января 2019

Как и Говинд Пармар уже предложил , лучше / проще использовать fgets() для чтения полной строки ввода, чем использовать scanf() и др. для интерактивного ввода человеком.

Основная причина заключается в том, что по умолчанию интерактивный стандартный ввод буферизуется по строкам (и его изменение нетривиально). Таким образом, когда пользователь начинает вводить свой ввод, он не сразу предоставляется вашей программе; только когда пользователь нажимает Enter .

Если мы прочитаем каждую строку ввода, используя fgets(), мы можем затем отсканировать и преобразовать ее, используя sscanf(), которая работает так же, как scanf() / fscanf(), за исключением того, что sscanf() работает на строковом вводе, а не на входном потоке.

Вот практический пример:

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

#define  MAX_LINE_LEN  100

int main(void)
{
    char   buffer[MAX_LINE_LEN + 1];
    char  *line, dummy;
    double value;

    while (1) {

        printf("Please type a number, or Q to exit:\n");
        fflush(stdout);

        line = fgets(buffer, sizeof buffer, stdin);
        if (!line) {
             printf("No more input; exiting.\n");
             break;
        }

        if (sscanf(line, " %lf %c", &value, &dummy) == 1) {
             printf("You typed %.6f\n", value);
             continue;
        }

        if (line[0] == 'q' || line[0] == 'Q') {
             printf("Thank you; now quitting.\n");
             break;
        }

        printf("Sorry, I couldn't parse that.\n");
    }

    return EXIT_SUCCESS;
}

fflush(stdout); не является необходимым, но и не причиняет вреда. Это в основном гарантирует, что все, что у нас есть printf() 'd или записано в stdout, будет сброшено в файл или устройство; в этом случае это будет отображаться в терминале. (В этом нет необходимости, поскольку стандартный вывод по умолчанию также является буферизованной строкой, поэтому \n в шаблоне printf, печатающем новую строку, также вызывает сброс.

Мне действительно нравится разбрасывать эти fflush() вызовы, где бы мне ни приходилось помнить, что на данный момент важно, чтобы весь вывод был фактически сброшен в вывод, а не кэширован библиотекой C. В этом случае мы определенно хотим, чтобы приглашение было видимым для пользователя, прежде чем мы начнем ждать его ввода!

(Но, опять же, потому что printf("...\n"); до его завершения новой строкой, \n, и мы не изменили стандартную выходную буферизацию, fflush(stdout); там не нужен.)

Строка line = fgets(buffer, sizeof buffer, stdin); содержит несколько важных деталей:

  • Мы определили макрос MAX_LINE_LEN ранее, потому что fgets() может читать только строку, если указан буфер, и будет возвращать остаток этой строки в следующих вызовах.

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

  • +1 в char buffer[MAX_LINE_LEN + 1]; - потому что строки в C заканчиваются нулевым символом, '\0', в конце. Таким образом, если у нас есть буфер из 19 символов, он может содержать строку максимум из 18 символов.

  • Обратите внимание, что NUL или nul с одним ell является именем символа ASCII с кодом 0, '\0' и является символом маркера конца строки.

    NULL (или иногда ноль), однако, является указателем на нулевой адрес, а в C99 и более поздних версиях совпадает с (void *)0. Это сторожевое значение и значение ошибки, которое мы используем, когда мы хотим установить указатель на распознаваемое значение ошибки / неиспользуемого / пустого значения вместо указания на фактические данные.

  • sizeof buffer - это общее количество символов (включая nul char в конце строки), используемое переменной buffer.

    В этом случае мы могли бы использовать MAX_LINE_LEN + 1 вместо этого (второй параметр fgets() - это количество символов в данном буфере, , включая резервирование для конца строковый символ).

    Причина, по которой я использовал sizeof buffer, заключается в том, что это так полезно. (Помните, что если бы buffer был указателем, а не массивом, он оценивал бы размер указателя; не количество данных, доступных для того, на что указывает указатель. Если вы используете указатели, вам нужно будет отслеживать количество памяти, доступной там самостоятельно, обычно в отдельной переменной. Именно так работает Си.)

    А также потому, что важно, чтобы sizeof был не функцией, а оператором: он не оценивает свой аргумент, он учитывает только размер (типа) аргумента. Это означает, что если вы сделаете что-то глупое, например sizeof (i++), вы обнаружите, что i не увеличено, и что оно дает то же значение, что и sizeof i. Опять же, это потому, что sizeof является оператором, а не функцией, и он просто возвращает размер своего аргумента.

  • fgets() возвращает указатель на строку, сохраненную в буфере, или NULL, если произошла ошибка.

    Именно поэтому я назвал указатель line и массив хранения buffer. Они описывают мои намерения как программиста. (Кстати, это очень важно при написании комментариев: не описывайте, что делает код, потому что мы можем прочитать код; но опишите ваше намерение относительно того, что код должен делать, потому что только программист знает это, но это важно знать это намерение, если кто-то пытается понять, изменить или исправить код.)

  • Семейство функций scanf () возвращает количество успешных преобразований. Чтобы обнаружить ввод, в котором правильное числовое значение сопровождается мусором, скажем 1.0 x, я попросил sscanf() игнорировать любые пробелы после числа (пробел означает символы табуляции, пробелы и символы новой строки; '\t', '\n', '\v', '\f', '\r' и ' ' для языка C по умолчанию, использующего набор символов ASCII), и попробуйте преобразовать один дополнительный символ, dummy.

    Теперь, если в строке есть что-то, кроме пробела после числа, sscanf() сохранит первый символ этого чего-либо в dummy и вернет 2. Однако, потому что я хочу только строки, которые содержат только число и нет фиктивных символов, я ожидаю возвращаемое значение 1.

  • Чтобы обнаружить q или Q (но только как первый символ в строке), мы просто исследуем первый символ в line, line[0].

    Если бы мы включили <string.h>, мы могли бы использовать, например, if (strchr(line, 'q') || strchr(line, 'Q')) чтобы увидеть, есть ли где-нибудь в поставляемой строке q или Q. strchr(string, char) возвращает указатель на первое вхождение char в строку или NULL, если его нет; и все указатели, кроме NULL, считаются логически верными. (То есть мы могли бы эквивалентно написать if (strchr(line, 'q') != NULL || strchr(line, 'Q') != NULL).)

    Другая функция, которую мы могли бы использовать, объявленная в <string.h>, это strstr(). Он работает как strchr(), но вторым параметром является строка. Например, (strstr(line, "exit")) имеет значение true, только если line где-то содержит exit. (Это может быть brexit или exitology, хотя; это просто поиск по подстроке.)

  • В цикле continue пропускает остаток тела цикла и начинает следующую итерацию тела цикла с начала.

  • В цикле break пропускает остаток тела цикла и продолжает выполнение после цикла.

  • EXIT_SUCCESS и EXIT_FAILURE - стандартные коды состояния выхода, определяемые <stdlib.h>. Большинство предпочитают использовать 0 для EXIT_SUCCESS (потому что именно так оно и есть в большинстве операционных систем), но я думаю, что такое описание успеха / неудачи облегчает чтение кода.

0 голосов
/ 25 января 2019

Я бы не использовал scanf -семейные функции для чтения из stdin в целом.

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

Однако, если спецификация для вашего задания или домашнего задания требует использования scanf с %f в качестве спецификатора формата, вы можете проверить его возвращаемое значение, которое будет содержать счетчик числа формата Спецификаторы в строке формата, которые были успешно отсканированы:

& секта; 7.21.6.2

Функция [scanf] возвращает значение макроса EOF в случае сбоя ввода до завершения первого преобразования (если оно есть). В противном случае функция возвращает количество назначенных элементов ввода, которое может быть меньше предусмотренного или даже равно нулю в событие раннего неудачного сопоставления.

Оттуда вы можете диагностировать, действителен ли ввод или нет. Кроме того, когда scanf терпит неудачу, stdin не очищается, и последующие вызовы к scanf (то есть в цикле) продолжат видеть, что там находится. Этот вопрос содержит некоторую информацию о том, как с этим справиться.

...