Dismember full numerical expression in C

Asked

Viewed 784 times

1

Hello; I have a code that should be done in the C language, which is to receive a numerical expression, including the result of it, for example: "20+30=50", which will be stored in a string. After receiving the expression, I wish to break up this expression by placing each number into an int variable, and the operators ("+" and "=", in this case) into char type variables and present each one separately in their respective int and char variables. I can do what I want by using only the first number ("20"), the first operator ("+") and the equality operator ("=") that I can store in the correct variables, the other numbers are empty. Following is my code for you to analyze and help me, it is not complete yet, I need to make this reading and "dismemberment" of the string work to complete the program. I’ve researched a lot of places, I’ve asked a lot of questions with a lot of people, and nothing’s been solved yet. Anyone who can point out the error, present me with a new solution, I will be very grateful.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{
    char expressao[20];
    int tamExpressao;
    int aux1;
    int aux2;
    int aux3;
    int aux4;
    char num1[3]={0};
    char num2[3]={0};
    char num3[3]={0};
    char oper1;
    char operIgual;
    int numero1=0;
    int numero2=0;
    int numero3=0;

    printf("Digite expressao:\n");
    scanf("%s", expressao);

    printf("%s\n\n", expressao);
    tamExpressao=strlen(expressao);
    printf("%i\n\n", tamExpressao);

         for (aux1=0; aux1<tamExpressao; aux1++)
         {
             if  (expressao[aux1]=='0'|expressao[aux1]=='1'|expressao[aux1]=='2'|expressao[aux1]=='3'|expressao[aux1]=='4'|expressao[aux1]=='5'|expressao[aux1]=='6'|expressao[aux1]=='7'|expressao[aux1]=='8'|expressao[aux1]=='9')
             {
                 for (aux2=0; aux2<3; aux2++)
                 {
                     if (numero1>0)
                     {
                         if (numero2>0)
                         {
                             num3[aux2]=num3[3]+expressao[aux1];
                         } else {num2[aux2]=num2[3]+expressao[aux1];}
                     } else {num1[aux2]=num1[3]+expressao[aux1];}
                     }
                     } else if  (expressao[aux1]=='+'|expressao[aux1]=='-')
                     {
                        numero1=atoi(&num1[aux2]);
                        oper1=expressao[aux1];
                        } else if  (expressao[aux1]=='=')
                            {
                                numero2=atoi(&num2[aux2]);
                                operIgual=expressao[aux1];
                            }
         }
         numero3=atoi(&num3[3]);
                        //Apresentação dos números digitados
                        printf("%i\t%i\t%c\t%i\t%c\t%i\n", aux1, numero1, oper1, numero2, operIgual, numero3);
                        //---

    system("PAUSE");
    return 0;
}
  • Can’t you format the code a little better? You don’t even want to touch something like that. It helps you not understand what you’re doing. I was trying to read and got lost in it.

  • 1

    Consider using strtoull() Vz de atoi(). With the first function get a much more powerful error handling.

  • Use || and && (short circuit operators) instead of | and & in your ifs. Also, it has a ctype header. h with functions like isdigit and isspace that are useful for you (and more efficient than your ifs as well)

1 answer

2

I don’t know if it’s worth trying to adapt your code to make it work. It will not work very well to have a big loop on the outside being that in each iteration you want to do something different (in the first you want to save the number in the first variable, in the second you want to save the operator, in the third you want to catch another number, etc). The traditional way to solve your problem is to create a separate "lexer" that reads a "token" at a time.

First we can define a type of data to represent an element of its expression:

enum TIPO_TOKEN = {
    TK_NUM,
    TK_OP,
    TK_EQ,
}

In addition to the type, you will need a place to store the "value" of the read object. A simple way (but gambiarrosa) is to use even global variables:

TIPO_TOKEN token_type;
int number_value;
char op_value;

In addition, we will need to maintain the reading state (which is the first unread position of our input). In C the normal would be to use a char pointer to do this - so you don’t have to deal with indexes all the time. Also, as strings in C are terminated by a\0, you don’t have to make a strlen to see when your input ends.

char * next_char;

With this in hand, you can try to write a function that reads one element at a time, and saves the values in those global:

void init_lexer(char *str){
    next_char = str;
}

int next_token(){ 
    if(*next_char == '='){
        token_type = TK_EQ;
        ++next_char;
        return 0; //OK
    }else if(*next_char == '+' || *next_char == '-'){
        token_type = TK_OP;
        op_value = *next_char;
        ++next_char;
        return 0;
    }else if(isdigit(*next_char)){
        token_type = TK_NUMBER;
        num_value = /* ...*/ ;
        /*...*/
        return 0;
    }else{
       /*...*/
    }
}

Once you have implemented lexer, your main is much easier to write:

int n1;
char op;
int n2;
int n3;

init_lexer(entrada);
if(next_token()){ /* erro: não conseguiu ler token */ }
if(token_type != TK_NUM){ /* erro: entrada nao comeca com numero */ }
n1 = num_value;
if(next_token()){ /* erro */ }
if(token_type != TK_OP){ /* erro */ }
op = op_value;
/*...*/

summarizing: The important thing here is that it is worth separating your code into a lexer, the routine that converts the text input to a sequence of structured "tokens" (the lexer) and a parser, the routine responsible for consuming this sequence of tokens and checking if they are in the right order (for example, "10 + + 5" is a sequence of tokens that makes no sense).

The specifics can vary a lot - you can use global variables or not, Unions to store the token value, etc. Moreover, there are external tools such as flex and Bison that are very useful if your program starts to get more complicated (for example, flex lets you write the lexer specification using regular expressions)

  • I really appreciate the help. However, as it is a college job, however feasible, the use of pointers to solve this work is not accepted. Isn’t there a way I can do what I want without the use of pointers? I’m really racking my brain, and nothing I try is working. (Don’t worry, I’m not asking you to do the work for me, it’s much more complex than that, this is just a part of what will be implemented in the final work). Thank you!

  • Instead of using a pointer to mark the current position, you can use a combination "string start pointer + integer index", which you were not doing before. I think deep down you will still need to use pointers if you want to be able to access the vector expressão out of main but using an entire index you avoid doing arithmetic manipulations on the pointers (which is the most complicated).

Browser other questions tagged

You are not signed in. Login or sign up in order to post.