Untitled

 avatar
unknown
plain_text
2 months ago
14 kB
2
Indexable
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>

////////////////////////////////////////////
// 1 В начале программы задаём нужный тип.
////////////////////////////////////////////
//#define our_type unsigned short int
//#define our_type short
//#define our_type unsigned long long
#define our_type int  // Выберите нужный тип

////////////////////////////////////////////
// Вспомогательные макросы для проверки,
// является ли выбранный тип знаковым.
////////////////////////////////////////////

static int is_our_type_signed(void) {
    // Если (our_type)-1 < 0, значит тип — знаковый
    volatile our_type test_val = (our_type)-1;
    return (test_val < 0);
}

////////////////////////////////////////////
// Функции для получения границ заданного типа.
// Работают для типов, размер которых <= 64 бит.
// (для учебных целей обычно хватает)
////////////////////////////////////////////

static long long get_min_our_type(void) {
    // Для знаковых типов вычислим минимальное значение.
    // Если тип беззнаковый — минимум 0.
    if (is_our_type_signed()) {
        // Допустим, sizeof(our_type) <= sizeof(long long)
        // Построим минимум как -(1 << (бит - 1)).
        int bits = (int)(sizeof(our_type) * 8);
        // Учитываем, что 1LL << 31 при 32 битах может быть.
        // Для 64 бит тоже допустимо.
        long long limit = (1LL << (bits - 1));
        return -limit;
    } else {
        return 0;
    }
}

static unsigned long long get_max_our_type(void) {
    // Для знакового типа: (1 << (бит - 1)) - 1
    // Для беззнакового: (1 << (бит)) - 1
    int bits = (int)(sizeof(our_type) * 8);
    if (is_our_type_signed()) {
        unsigned long long limit = (1ULL << (bits - 1)) - 1ULL;
        return limit;
    } else {
        unsigned long long limit = (1ULL << bits) - 1ULL;
        return limit;
    }
}

////////////////////////////////////////////
// Проверка, поместится ли строка s
// (которая может содержать знак) в our_type
////////////////////////////////////////////

static int is_in_range(const char *s) {
    // Если строка слишком长或者 пустая — сразу нет
    if (!s || !(*s)) {
        return 0;
    }

    // Удаляем начальные пробелы
    while (*s == ' ' || *s == '\t') s++;
    if (!(*s)) return 0;

    // Определим границы
    long long min_val = get_min_our_type();
    unsigned long long max_val = get_max_our_type();

    // Используем strtoll/strtoull в зависимости от знаковости
    if (is_our_type_signed()) {
        // Проверяем, действительно ли это валидная форма для знакового
        char *endptr = NULL;
        long long val = strtoll(s, &endptr, 10);
        // endptr должен указывать на конец строки (без учёта пробелов)
        while (*endptr == ' ' || *endptr == '\t') endptr++;
        if (*endptr != '\0') {
            // Значит, есть лишние символы
            return 0;
        }
        // Проверяем диапазон
        if (val < min_val || val > (long long)max_val) {
            return 0;
        }
        return 1;
    } else {
        // Беззнаковый тип
        // Строка не должна начинаться с '-'
        if (*s == '-') {
            // отрицательное число для беззнакового типа
            return 0;
        }
        char *endptr = NULL;
        unsigned long long val = strtoull(s, &endptr, 10);
        while (*endptr == ' ' || *endptr == '\t') endptr++;
        if (*endptr != '\0') {
            // Лишние символы
            return 0;
        }
        if (val > max_val) {
            return 0;
        }
        return 1;
    }
}

////////////////////////////////////////////
// Проверка ведущих нулей и прочих ограничений:
// "Пустое число недопустимо, минус без числа недопустим, после минуса цифра 0 недопустима,
//  незначащие нули недопустимы (можно 0, 1, 234, но нельзя 01 или 02)."
////////////////////////////////////////////

static int validate_format(const char *s) {
    // Пустая строка — невалидно
    if (!s || !*s) return 0;

    // Проверим, есть ли минус
    int signed_t = is_our_type_signed();
    int negative = (s[0] == '-');

    // Если тип беззнаковый, минус сразу невалиден
    if (!signed_t && negative) {
        return 0;
    }

    // Сдвинемся, если есть минус
    const char *p = s;
    if (negative) {
        p++;
        // "минус без числа недопустим"
        if (*p == '\0') {
            // строка пустая после минуса
            return 0;
        }
        // "после минуса цифра 0 недопустима" (т.е. нельзя -0, -01, …)
        // но разрешаем ровно "-0"? ТЗ говорит "после минуса цифра 0 недопустима",
        // значит вообще нельзя "-0"?
        // ТЗ: "…отрицательное число должно начинаться не с нуля".
        // Значит -0 тоже невалидно.
        if (*p == '0') {
            return 0;
        }
    }

    // Теперь проверяем ведущие нули для положительного
    // "можно ввести 0, 1, 234, но нельзя 01 или 02"
    if (!negative) {
        // если первый символ '0' и строка не является просто "0"
        if (p[0] == '0' && p[1] != '\0') {
            // невалидно
            return 0;
        }
    }

    // Остальные символы должны быть только цифры
    // (минус уже учли)
    for (; *p; p++) {
        if (!isdigit((unsigned char)*p)) {
            return 0;
        }
    }

    return 1;
}

////////////////////////////////////////////
// Основная функция, которая:
// 1) Читает посимвольно с клавиатуры
// 2) Корректно отображает вводимый символ или Backspace
// 3) Не даёт ввести лишнее (не влезающее в тип и т.п.)
// 4) Возвращает результат, когда пользователь ввёл валидное число
////////////////////////////////////////////

our_type input_number(void) {
    char buffer[1024]; // достаточно большой для учебных целей
    int len = 0;
    memset(buffer, 0, sizeof(buffer));

    // Вывод приглашения (без перевода строки по условию)
    // printf("Enter the number:");
    // fflush(stdout);

    while (1) {
        int c = getchar();
        if (c == '\n' || c == '\r') {
            // Пользователь нажал Enter
            // Проверим — если строка валидна и формат верен, выходим.
            // Иначе игнорируем Enter
            if (len > 0 && validate_format(buffer) && is_in_range(buffer)) {
                // Валидно
                break;
            } else {
                // Не отпускаем пользователя
                continue; // снова ждать ввода
            }
        } else if (c == EOF) {
            // На всякий случай, если EOF — прервём
            // Можно по-другому обрабатывать
            break;
        } else if (c == '\b' || c == 127) {
            // Обработка Backspace
            if (len > 0) {
                // Стираем символ с экрана
                // На многих системах можно вывести "\b \b" или так:
                printf("\b \b");
                fflush(stdout);
                buffer[--len] = '\0';
            }
        } else {
            // Обработка обычного символа
            // Проверим, если это допустимо (цифры или минус — если тип знаковый и в начале)
            // Но сначала поставим кандидат
            if (len < (int)sizeof(buffer) - 1) {
                char candidate = (char)c;
                // проверим, если candidate — минус или цифра
                // допустим минус только в начале, если тип знаковый
                if (candidate == '-' && is_our_type_signed()) {
                    // минус только если buffer пока пуст
                    if (len == 0) {
                        // Попробуем добавить и проверить
                        buffer[len] = candidate;
                        buffer[len+1] = '\0';
                        // Проверим формат и диапазон (хотя диапазон с одним минусом не валиден),
                        // но нам важно, чтобы на экране не было того, что "не влезает"
                        // Условие: "ни в какой момент…"
                        // Однако минус сам по себе ещё не полное число.
                        // Мы разрешим его отобразить, если формат позволяет.
                        // Но формат не позволяет пустое число при Enter,
                        // так что при вводе минуса — мы ещё не завершимся.

                        // Проверим, действительно ли это не нарушает правило
                        // "не отображать минус для беззнакового" — уже проверили, is_our_type_signed.
                        // Пока отобразим, но если пользователь нажмёт Enter сейчас,
                        // будет недопустимо — и продолжим.

                        printf("%c", candidate);
                        fflush(stdout);
                        len++;
                    } else {
                        // Минус не в начале — игнорируем
                    }
                } else if (isdigit((unsigned char)candidate)) {
                    // Попробуем добавить
                    buffer[len] = candidate;
                    buffer[len + 1] = '\0';
                    // Теперь проверим:
                    // 1) формат: ведущие нули, минус и т.п.
                    // 2) входит ли в диапазон

                    if (validate_format(buffer) && is_in_range(buffer)) {
                        // Тогда можно реально добавить символ
                        printf("%c", candidate);
                        fflush(stdout);
                        len++;
                    } else {
                        // Иначе игнорируем (не добавляем), восстанавливаем старую строку
                        buffer[len] = '\0';
                    }
                } else {
                    // любой другой символ игнорируем
                }
            }
        }
    }

    // Когда вышли из цикла, buffer — корректная строка
    // Преобразуем в число
    // Учитывая, что is_in_range вернуло true, здесь точно помещается в our_type

    if (is_our_type_signed()) {
        long long val = strtoll(buffer, NULL, 10);
        return (our_type)val;
    } else {
        unsigned long long val = strtoull(buffer, NULL, 10);
        return (our_type)val;
    }
}

////////////////////////////////////////////
// Тестовая main
////////////////////////////////////////////
int main(void) {
    printf("Enter the number:");
    fflush(stdout);

    our_type num = input_number();
    printf("\nYou entered: %lld\n", (long long)num);

    return 0;
}
Editor is loading...
Leave a Comment