Проверить корректность арифметического выражения, заданного строкой - C (СИ)

Узнай цену своей работы

Формулировка задачи:

Задано строку, которая представляет арифметическое выражение. Проверить корректность заданного выражения. Вычислить его.

Решение задачи: «Проверить корректность арифметического выражения, заданного строкой»

textual
Листинг программы
#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;
}

Объяснение кода листинга программы

Этот код - это математический калькулятор, который проверяет и вычисляет выражения, заданные в виде строк. Он поддерживает основные арифметические операции, а также скобки для группировки выражений. Код также проверяет ошибки, такие как деление на ноль и неправильное использование скобок. Синтаксис кода:

  1. include - подключает стандартную библиотеку для ввода и вывода данных.

  2. include - подключает стандартную библиотеку для работы с числами.

  3. include - подключает стандартную библиотеку для работы со строками и символами.

  4. 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 некорректно синтаксически.

Оцени полезность:

10   голосов , оценка 4 из 5
Похожие ответы