Untitled
unknown
plain_text
8 months ago
14 kB
3
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