Проверить корректность арифметического выражения, заданного строкой - C (СИ)
Формулировка задачи:
Решение задачи: «Проверить корректность арифметического выражения, заданного строкой»
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
//коды ошибок
enum err_expr {
ERR_SUCCESS, //нет ошибки
ERR_NONE_STR, //строка пуста
ERR_DIV_BYZERO, //деление на нуль!
ERR_PARSE_NUMBER, //ошибка при парсирование числа
ERR_EXPR //другие ошибки: скобки, иные символы...
//...
};
int is_expr(const char* s);
double _calc(const char* s, const char** p, enum err_expr* e);
//вычисление мат-выражений из строки
double calculate(char* s, enum err_expr* err){
char* a, *b;
enum err_expr e = ERR_SUCCESS;
const char* p = NULL;
double n = 0.0;
if(*s && is_expr(s)){
a = s;
while(*a && !isspace(*a))
++a;
for(b = a; *a; *a = *(++b)){
if(! isspace(*b))
++a;
}
if(*s)
n = _calc(s, &p, &e);
else
e = ERR_NONE_STR;
} else
e = (*s) ? ERR_EXPR : ERR_NONE_STR;
if(err != NULL)
*err = e;
return n;
}
int main(void){
enum err_expr e;
char s[256];
double t, res;
//1-ый пример
strcpy(s, "-(-(-(-(-(-(-(-(-(-0.5))))))) + 0.7))*5.123-(-(0.4*3.3/0.0001))/2.5");
t = -(-(-(-(-(-(-(-(-(-0.5))))))) + 0.7))*5.123-(-(0.4*3.3/0.0001))/2.5;
res = calculate(s, NULL);
printf("result: %lg\n", res);
printf("test : %lg\n\n", t);
//2-ой пример
strcpy(s, "-1.4e+5 + (-2) * (-(-5.5 + 0.1234)*(-5.2 / 0.1234 + 9.9) + 0.333) / 0.5");
t = -1.4e+5 + (-2) * (-(-5.5 + 0.1234)*(-5.2 / 0.1234 + 9.9) + 0.333) / 0.5;
res = calculate(s, NULL);
printf("result: %lg\n", res);
printf("test : %lg\n\n", t);
//3-ий пример
strcpy(s, "1. / 1000.0 * (-(-999.2-(-9.0e+3))+(-(0.5 + 5.5*200)/(-3.3) + 0.7) * 2.8)");
t = 1. / 1000.0 * (-(-999.2-(-9.0e+3))+(-(0.5 + 5.5*200)/(-3.3) + 0.7) * 2.8);
res = calculate(s, NULL);
printf("result: %lg\n", res);
printf("test : %lg\n\n", t);
//4-ый пример с обработкой ошибок
strcpy(s, "10 * 2 / 0");
res = calculate(s, &e);
switch(e){
case ERR_SUCCESS:
printf("%lg\n\n", res);
break;
case ERR_NONE_STR:
puts("строка пуста");
break;
case ERR_DIV_BYZERO:
puts("деление на нуль!");
break;
case ERR_PARSE_NUMBER:
puts("ошибка при парсирование числа.");
break;
case ERR_EXPR:
puts("другая ошибка.");
break;
}
getchar();
return 0;
}
//проверка скобок на правильность расстоновки
int is_expr(const char* s){
int n = 0;
for(; *s; ++s){
if(*s == '(')
++n;
else if(*s == ')'){
if(--n < 0)
break;
}
}
return (n == 0);
}
// рекурсивное вычисление мат-выражений из строки
double _calc(const char* s, const char** p, enum err_expr* e){
int neg;
char c, c1;
char* i;
const char* o;
double k, v, n = 0.0;
if((*s == '(') || (*s == '-' && *(s + 1) == '(')){
neg = (*s == '-');
n = _calc(s + (1 + neg), &o, e);
if(*e != ERR_SUCCESS)
return 0.0;
s = o;
if(neg)
n = 0.0 - n;
} else {
n = strtod(s, &i);
if(s == i){
*e = ERR_PARSE_NUMBER;
return 0.0;
}
s = i;
}
while(*s && (*s != ')')){
c = *s++;
if(! *s){
*e = ERR_EXPR;
return 0.0;
}
if(*s == '('){
k = _calc(s + 1, &o, e);
if(*e != ERR_SUCCESS)
return 0.0;
s = o;
} else {
k = strtod(s, &i);
if(s == i){
*e = ERR_PARSE_NUMBER;
return 0.0;
}
s = i;
}
switch(c){
case '*':
n *= k;
break;
case '/':
if(k == 0.0){
*e = ERR_DIV_BYZERO;
return 0.0;
}
n /= k;
break;
case '+':
case '-':
if((*s == '*') || (*s == '/')) {
v = k;
while(*s && (*s != ')')) {
c1 = *s;
if((c1 == '+') || (c1 == '-'))
break;
++s;
if(*s == '('){
k = _calc(s + 1, &o, e);
if(*e != ERR_SUCCESS)
return 0.0;
s = o;
} else {
k = strtod(s, &i);
if(s == i){
*e = ERR_PARSE_NUMBER;
return 0.0;
}
s = i;
}
if(c1 == '/') {
if(k == 0.0){
*e = ERR_DIV_BYZERO;
return 0.0;
}
v /= k;
} else if(c1 == '*')
v *= k;
}
k = v;
}
if(c == '+')
n += k;
else if(c == '-')
n -= k;
if(*s == ')'){
*p = s + 1;
return n;
}
break;
}
}
if(*s == ')')
*p = s + 1;
return n;
}
Объяснение кода листинга программы
Этот код - это математический калькулятор, который проверяет и вычисляет выражения, заданные в виде строк. Он поддерживает основные арифметические операции, а также скобки для группировки выражений. Код также проверяет ошибки, такие как деление на ноль и неправильное использование скобок. Синтаксис кода:
-
include
- подключает стандартную библиотеку для ввода и вывода данных. -
include
- подключает стандартную библиотеку для работы с числами. -
include
- подключает стандартную библиотеку для работы со строками и символами. -
include
- подключает стандартную библиотеку для работы со строками. Основная функция - это функция calculate, которая принимает строку с математическим выражением, указатель на переменную, в которую будет сохранено результат, и указатель на ошибку. Функция parse_number - это функция, которая преобразует строку в число. Код использует стек для обработки выражений. Когда функция is_expr проверяет, что выражение корректно, она создает стек для хранения операндов и операций. Когда она встречает операцию, она помещает ее на вершину стека, а затем продолжает работу с предыдущими операндами. Когда функция _calc встречает число, она вызывает функцию parse_number для преобразования строки в число, а затем добавляет это число в стек. Когда функция _calc встречает операцию, она проверяет, что стек не пуст, и затем выполняет операцию. Если происходит ошибка, она возвращает значение ошибки. Функция main - это точка входа в программу. Она демонстрирует использование калькулятора, вызывая функцию calculate для трех примеров. В первом примере она вычисляет выражение
-(-(-5.5 + 0.1234)*(-5.2 / 0.1234 + 9.9) + 0.333) / 0.5. Во втором примере она вычисляет выражение-1.4e+5 + (-2) * (-(-5.5 + 0.1234)*(-5.2 / 0.1234 + 9.9) + 0.333) / 0.5. В третьем примере она вычисляет выражение1. / 1000.0 * (-(-999.2-(-9.0e+3))+(-(0.5 + 5.5*200)/(-3.3) + 0.7) * 2.8). В третьем примере программа выдает ошибку, потому что деление на ноль невозможно. В четвертом примере программа выдает ошибку, потому что выражение10 * 2 / 0некорректно синтаксически.