Обновить du3/program.c

This commit is contained in:
Bohdana Marchenko 2025-03-12 19:56:28 +00:00
parent c9f4b35f7a
commit f855b9081c

View File

@ -1,263 +1,111 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
#define EPSILON 0.001 // Tolerancia pre porovnanie reálnych čísel: ak je rozdiel menší alebo rovný EPSILON, považujeme čísla za rovnaké.
#define EPSILON 0.001
#define BUFFER_SIZE 100
/*
* Funkcia trim_newline
* --------------------
* Odstraňuje znak nového riadku ('\n') zo zadaného reťazca, ak je prítomný.
* Pri načítavaní riadkov pomocou fgets sa totiž často na konci reťazca nachádza '\n',
* ktorý by mohol ovplyvniť ďalšie spracovanie.
*
* Parametre:
* - line: Reťazec, z ktorého sa odstrániť koncový znak nového riadku.
*/
void trim_newline(char *line) {
size_t len = strlen(line);
if (len > 0 && line[len - 1] == '\n') {
line[len - 1] = '\0';
}
}
/*
* Funkcia skip_spaces
* --------------------
* Preskakuje všetky whitespace znaky (medzery, tabulátory, nové riadky) v zadanom reťazci
* a vráti ukazovateľ na prvý znak, ktorý nie je whitespace.
*
* Parametre:
* - str: Reťazec, v ktorom chceme preskočiť medzery.
*
* Návratová hodnota:
* - Ukazovateľ na prvý znak, ktorý nie je whitespace.
*/
char* skip_spaces(char *str) {
while (*str && isspace((unsigned char)*str)) {
str++;
}
return str;
}
/*
* Funkcia parse_expression
* -------------------------
* Rozparsuje riadok obsahujúci matematický príklad vo formáte:
*
* ČÍSLO OPERÁCIA ČÍSLO = VÝSLEDOK
*
* Medzi jednotlivými časťami môže byť ľubovoľný počet medzier.
*
* Parametre:
* - line: Reťazec obsahujúci matematický príklad, napr. "1.1 + 2.2 = 3.3"
* - num1: Ukazovateľ, do ktorého sa uloží prvé číslo.
* - op: Ukazovateľ, do ktorého sa uloží operátor ('+', '-', '*' alebo '/').
* - num2: Ukazovateľ, do ktorého sa uloží druhé číslo.
* - result: Ukazovateľ, do ktorého sa uloží očakávaný výsledok.
*
* Návratová hodnota:
* - 0, ak sa parsovanie podarilo úspešne.
* - 1, ak nastala chyba (nesprávny formát, chýbajúce číslo, operátor alebo '=')
*
* Príklad vstupu:
* " 10.0 / 2.0 = 5.0"
*/
int parse_expression(char *line, float *num1, char *op, float *num2, float *result) {
char *ptr = line;
// Preskočíme úvodné medzery a načítame prvé číslo
ptr = skip_spaces(ptr);
char *endPtr;
*num1 = strtof(ptr, &endPtr);
if (ptr == endPtr) {
return 1; // Chyba: nepodarilo sa načítať číslo
}
ptr = endPtr;
// Preskočíme medzery a načítame operátor
ptr = skip_spaces(ptr);
if (*ptr == '\0') return 1;
*op = *ptr;
if (*op != '+' && *op != '-' && *op != '*' && *op != '/') {
return 1; // Chyba: operátor nie je platný
}
ptr++;
// Preskočíme medzery a načítame druhé číslo
ptr = skip_spaces(ptr);
*num2 = strtof(ptr, &endPtr);
if (ptr == endPtr) {
return 1; // Chyba: nepodarilo sa načítať druhé číslo
}
ptr = endPtr;
// Preskočíme medzery a očakávame znak '='
ptr = skip_spaces(ptr);
if (*ptr != '=') {
return 1; // Chyba: chýba znak '='
}
ptr++; // Preskočíme '='
// Preskočíme medzery a načítame výsledok
ptr = skip_spaces(ptr);
*result = strtof(ptr, &endPtr);
if (ptr == endPtr) {
return 1; // Chyba: nepodarilo sa načítať výsledok
}
ptr = endPtr;
// Overíme, že za výsledkom nie sú žiadne neočakávané znaky (okrem medzier)
ptr = skip_spaces(ptr);
if (*ptr != '\0') {
return 1; // Chyba: neočakávané znaky za výsledkom
}
return 0; // Úspešné parsovanie
}
/*
* Funkcia calculate
* -----------------
* Vykoná aritmetickú operáciu medzi dvoma číslami podľa zadaného operátora.
*
* Parametre:
* - num1: Prvé číslo.
* - op: Operátor ('+', '-', '*' alebo '/').
* - num2: Druhé číslo.
* - error: Ukazovateľ, do ktorého sa uloží 1, ak nastane chyba (napr. delenie nulou).
*
* Návratová hodnota:
* - Výsledok operácie, ak prebehne úspešne.
* - Ak nastane chyba, error je nastavený na 1 a funkcia vráti 0.
*
* Príklad:
* calculate(10.0, '/', 2.0, &error) vráti 5.0, error zostáva 0.
*/
float calculate(float num1, char op, float num2, int *error) {
*error = 0; // Inicializácia chyby na 0 (žiadna chyba)
float res;
switch(op) {
case '+':
res = num1 + num2;
break;
case '-':
res = num1 - num2;
break;
case '*':
res = num1 * num2;
break;
case '/':
// Kontrola delenia nulou; ak num2 je takmer 0, signalizujeme chybu
if (fabs(num2) < EPSILON) {
*error = 1; // Chyba: delenie nulou
return 0;
}
res = num1 / num2;
break;
default:
*error = 1;
// Функция вычисления результата
float vypocitaj(float a, float b, char op, int* error) {
if (op == '+') return a + b;
if (op == '-') return a - b;
if (op == '*') return a * b;
if (op == '/') {
if (b == 0) {
*error = 1; // Ошибка: деление на ноль
return 0;
}
return a / b;
}
return res;
*error = 1; // Ошибка: некорректный оператор
return 0;
}
/*
* Funkcia process_line
* ---------------------
* Spracuje jeden riadok vstupu obsahujúci matematický príklad a určí, či je zápis
* správny a či je výsledok správny.
*
* Postup:
* 1. Skopíruje vstupný riadok do lokálneho bufferu a odstráni prípadný koncový znak nového riadku.
* 2. Rozparsuje riadok na časti: prvé číslo, operátor, druhé číslo a očakávaný výsledok.
* 3. Ak parsovanie zlyhá, nastaví output na "CHYBA".
* 4. Vykoná operáciu a ak nastane chyba (napr. delenie nulou), nastaví output na "ZLE".
* 5. Zaokrúhli výsledok operácie na 2 desatinné miesta, pretože čísla zadané na max. 2 desatinné miesta.
* 6. Porovná zaokrúhlený výsledok s očakávaným výsledkom. Ak sa líšia o viac ako EPSILON,
* nastaví output na "ZLE", inak na "OK".
*
* Parametre:
* - line: Vstupný riadok (napr. "10.0 / 2.0 = 5.0").
* - output: Buffer, do ktorého sa uloží výsledný reťazec ("OK", "ZLE" alebo "CHYBA").
* - output_size: Veľkosť bufferu output.
*
* Príklad vstupu a výstupu:
* Vstup: "1 + 1 = 2"
* Výstup: "OK"
*/
void process_line(const char *line, char *output, size_t output_size) {
char buffer[256];
// Skopírujeme vstupný riadok do lokálneho bufferu
strncpy(buffer, line, 255);
buffer[255] = '\0'; // Zabezpečíme, že buffer je správne zakončený '\0'
trim_newline(buffer);
float num1, num2, givenResult;
char op;
// Pokúsime sa rozparsovať matematický príklad
if (parse_expression(buffer, &num1, &op, &num2, &givenResult) != 0) {
snprintf(output, output_size, "CHYBA");
return;
}
int calcError = 0;
float calculatedResult = calculate(num1, op, num2, &calcError);
if (calcError) {
// V prípade chyby (napr. delenie nulou) vypíšeme "ZLE"
snprintf(output, output_size, "ZLE");
return;
}
// Zaokrúhľujeme výsledok operácie na 2 desatinné miesta, pretože vstupné čísla majú max. 2 desatinné miesta.
float roundedResult = round(calculatedResult * 100) / 100;
// Porovnáme zaokrúhlený výsledok so zadaným výsledkom.
if (fabs(roundedResult - givenResult) <= EPSILON) {
snprintf(output, output_size, "OK");
// Функция проверки результата
void over_vysledok(float skutocny, float ocakavany) {
if (abs(skutocny - ocakavany) <= EPSILON) {
printf("OK\n");
} else {
snprintf(output, output_size, "ZLE");
printf("ZLE\n");
}
}
/*
* Hlavná funkcia programu.
* -------------------------
* Program načíta riadky zo štandardného vstupu. Každý riadok obsahuje matematický príklad vo formáte:
*
* ČÍSLO OPERÁCIA ČÍSLO = VÝSLEDOK
*
* Príklad vstupu:
* 10.0 / 2.0 = 5.0
* 1+2=3
* 3.333+4.667=8.0
*
* Pre každý neprázdny riadok program overí správnosť zápisu a výsledku a na štandardný výstup
* vypíše "OK", "ZLE" alebo "CHYBA".
*
* Ak sa načíta prázdny riadok (iba medzery), program sa ukončí.
*
* Ako skompilovať:
* gcc program.c -o program -lm
*
* Ako spustiť:
* ./program
*/
int main(int argc, char *argv[]) {
char buffer[256];
char output[32];
// Функция обработки одной строки
void spracuj_riadok(char* riadok) {
char* zaciatok = riadok;
char* koniec;
// Načítavame riadky zo štandardného vstupu (napr. z konzoly alebo súboru)
while (fgets(buffer, sizeof(buffer), stdin) != NULL) {
trim_newline(buffer);
// Ak je riadok prázdny (iba medzery), ukončíme cyklus a program
if (*skip_spaces(buffer) == '\0') {
// Читаем первое число
float a = strtof(zaciatok, &koniec);
if (zaciatok == koniec) {
printf("CHYBA\n");
return;
}
// Пропускаем пробелы перед оператором
while (*koniec == ' ') koniec++;
// Читаем оператор
char op = *koniec;
if (op != '+' && op != '-' && op != '*' && op != '/') {
printf("CHYBA\n");
return;
}
// Двигаем указатель дальше
koniec++;
while (*koniec == ' ') koniec++;
// Читаем второе число
float b = strtof(koniec, &koniec);
if (koniec == zaciatok) {
printf("CHYBA\n");
return;
}
// Пропускаем пробелы перед знаком "="
while (*koniec == ' ') koniec++;
if (*koniec != '=') {
printf("CHYBA\n");
return;
}
// Двигаем указатель дальше
koniec++;
while (*koniec == ' ') koniec++;
// Читаем результат
float vysledok = strtof(koniec, &koniec);
if (koniec == zaciatok) {
printf("CHYBA\n");
return;
}
// Вычисляем реальный результат
int error = 0;
float skutocny = vypocitaj(a, b, op, &error);
if (error) {
printf("CHYBA\n");
return;
}
// Проверяем правильность ответа
over_vysledok(skutocny, vysledok);
}
int main() {
char buffer[BUFFER_SIZE];
while (1) {
printf("Zadajte priklad:\n");
if (!fgets(buffer, sizeof(buffer), stdin)) break; // Проверяем конец ввода
if (buffer[0] == '\n') {
printf("KONIEC\n");
break;
}
process_line(buffer, output, sizeof(output));
printf("%s\n", output);
spracuj_riadok(buffer);
}
return 0;