Как сохранить значения с минимальным и максимальным размером в c

Я пытаюсь создать программу, которая запрашивает 13-16-значный номер кредитной карты и повторно запрашивает, вводит ли пользователь нечисловые значения. Пока моя программа работает, когда я ввожу 16 числовые значения; однако он повторно запрашивает, если я ввожу что-то меньшее.

Как заставить мою программу принимать минимум 13 цифр и максимум 16 цифр?

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

int main(void)
{
    long cn;
    char buf[18], *endptr;

    do
    {
        printf("card number please: ");
        fgets(buf, 17, stdin);
        cn = strtol(buf, &endptr, 0);
    } while (*endptr != '\0' || endptr == buf);

    printf("%ld\n", cn);
}

person Patrick Dankyi    schedule 13.04.2020    source источник
comment
Функция fgets сохраняет символ новой строки '\n' в конце строки. Он также читает максимум на один символ меньше указанного, усекая ввод, если он не подходит. Это означает, что для короткого номера символ после номера будет '\n', а не '\0'. Исключение составляет шестнадцатизначное число без пробелов: здесь '\n' не подходит и не включается в строку.   -  person M Oehm    schedule 13.04.2020
comment
(Длина буфера, передаваемая в fgets, включает в себя место для нулевого терминатора, так что вы могли бы fgets(buf, 18, stdin) или, может быть, даже лучше fgets(buf, sizeof(buf), stdin). Вы также можете сделать буфер намного больше и принимать дополнительные пробелы и дефисы, потому что люди склонны форматировать свои номера копий в четырехзначные фрагменты.)   -  person M Oehm    schedule 13.04.2020
comment
Aa в сторону: если вы используете strtol для проверки того, является ли ввод допустимым числом, вы должны явно использовать основание 10. В противном случае вы можете быть удивлены, если у вас есть номер копии с начальным нулем и с восьмерками и девятками в нем...   -  person M Oehm    schedule 13.04.2020


Ответы (2)


Вы можете использовать strcspn, чтобы найти конец массива char в while, вы можете вернуть размер массива char, пока он не найдет '\0' (или '\n', поскольку вы, похоже, не удаляете его из массива), затем вы можете использовать это, чтобы установить минимальный размер для ввода.

#include <string.h>
//...
do{
//...
while (strcspn(buf, "") < 14); //less than 13 chars will repeat the cycle
//...

Вы также можете удалить '\n' из buf, и в этом случае вы можете использовать strlen:

//...
do{
    fgets(buf, 17, stdin);
    buf[strcspn(buf, "\n")] = '\0';
while (strlen(buf) < 13); //by removing '\n' you can use strlen
//... 

Максимальный размер buf будет 16, потому что вы ограничиваете размер в fgets до 17, поэтому он будет хранить 16 символов плюс нуль-терминатор, вы можете уменьшить размер вашего массива до 17, поскольку элемент 18th бесполезен.

Также имейте в виду, что long не всегда составляет 8 байт, https://en.wikibooks.org/wiki/C_Programming/limits.h

При этом существует проблема наследования кода, он потребляет все, афабетические символы, пробелы и т. д., поэтому, если у вас есть ввод 1234 1234 1234 1234, только 1234 будет преобразован strtol.

В приведенном ниже примере я удаляю все, что не является цифрой, и сохраняю все остальные характеристики:

Используемый пример

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

int main()
{
    long long cn; //long long is never smaller than 8 bytes
    int i, c;
    char buf[17];

    do {
        i = 0;
        printf("card number please: ");
        while ((c = fgetc(stdin)) != '\n' && c != EOF && strlen(buf) < 17) //max size 16
        {
            if (isdigit(c)) //only digits
                buf[i++] = c;
        }
        buf[i] = '\0';
    } while (strlen(buf) < 13); //min size 13

    cn = strtoll(buf, NULL, 10); //strtoll for long long

    printf("%lld\n", cn);
}
person anastaciu    schedule 13.04.2020
comment
"\0\n" должен работать так же, как "". И strcspn(buf, "") должно быть просто strlen(buf). - person KamilCuk; 13.04.2020
comment
@KamilCuk, да, это то же самое, только менее явно, strlen() может работать, но нам нужно удалить '\n' из buf. Я отредактировал ответ, чтобы решить эту проблему. - person anastaciu; 13.04.2020
comment
Извините, я не понимаю, что в этом явного. "\0\n" - \n и завершающий нулевой байт просто игнорируются и тратят память. \0\n мог бы работать, если бы это была функция memcspn, которая могла бы работать с нулевыми байтами внутри памяти. - person KamilCuk; 13.04.2020
comment
@KamilCuk, явный в том смысле, что он показывает, что мы ищем, но я понимаю вашу точку зрения. - person anastaciu; 14.04.2020
comment
Обратите внимание, что strcspn(buf, "") (вероятно) медленнее, чем strlen(buf), но дает тот же результат. Есть вероятность, что оптимизатор был обучен эквивалентности, но, скорее всего, это не оптимизация, которую он делает. - person Jonathan Leffler; 16.04.2020
comment
@JonathanLeffler, это кажется правильным, обратите внимание, что если в строке есть '\n', strlen(buf) будет считать это. - person anastaciu; 16.04.2020

Если мы конкретно говорим о номерах кредитных карт, возможно, вы захотите применить его только по количеству цифр, которые вводит пользователь, а не по общему количеству символов, поскольку номера кредитных карт часто вводятся с пробелами или черточки между ними.

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

int numberofdigits(char *str)
{
    int i = 0, n = 0;
    while(str[i])
    {
        n += !!(isdigit(str[i]));
        i++;
    }

    return n;
}

int main()
{
    char str[100];
    int digits; 

    do
    {
        printf("Enter a credit card number: ");
        fgets(str, 100, stdin);

        digits = numberofdigits(str);
        printf("Read %d digits.\n", digits);
    }
    while((digits < 13) || (digits > 16));

    printf("Thanks for entering \"%s\"\n", str);

    return 0;
}
person Govind Parmar    schedule 13.04.2020