Untitled
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