天天看點

c++ _int64轉字元串_C語言 仿JS的eval函數 解析字元串

C語言 利用字尾表達式解析字元串(符合c98标準,很容易移植到電腦上)

最近用98标準的C語言寫了個解析字元串,類似于JavaScript中的eval函數,感覺挺實用(移植到了電腦上,可以畫F(X,Y)==0這種圖像了),特此分享一下,大家可以使用。

有了這個函數,就能像js,python那樣輕松解析字元串,移植到電腦上就能實作更好玩的功能了,更友善,特此分享。

感謝這篇文章給的啟發,不會轉字尾表達式的可以看看這篇,寫的很詳細

波蘭式、逆波蘭式與表達式求值 - 刺猬的溫馴 - 部落格園

源代碼在最底下!

1、支援的函數

邏輯表達式:

>,<,==(注意是雙等于号),<=,>=,&(and),|(or),~(not)

注意,在本解析字元串程式中,使用者輸入值0代表邏輯false,非0代表邏輯true(true預設為1,也就是2==2傳回的值是1)

數學運算符:

+,-,*,/,%(求模),^(乘方),!(階乘),#(負号,一般會自動區分負号和減号)

符号:

括号,逗号

函數:

三角函數 sin(x) cos(x) tan(x) asin(x) acos(x) atan(x)

如果不在定義域則傳回0

随機數 rand(x,y)(傳回[x,y]間的整數,需要srand)

弧度轉角度 deg(x) 角度轉弧度 rad(x)

條件表達式 if(條件,x,y),其中當條件計算結果非0,則傳回表達式x的值,否則傳回表達式y的值

對數 log(x,y)傳回以x為底y的對數 ln(x)傳回以e為底的自然對數

自然數的n次方 exp(x),傳回e^x

最大最小值 max(x,y)傳回表達式x和表達式y的較大的一個,min相反

取符号 sign(x) 如果x大于等于0,傳回1,否則傳回-1

四舍五入 round(x) 向下取整 floor(x)

絕對值 abs(x) 傳回x的絕對值

開平方 sqrt(x) 傳回x開平方的值

開n次方可以用運算符“ ^ ”代替

2、用法

#include<eval.c>
           

很簡單,就一行:

直接調用

double eval(char* str);
           

其中str為表達式,以’ 0 '結束

3、效果

計算0.5+6+3*5的結果為21.5

中間兩行分别是中綴表達式和字尾表達式

c++ _int64轉字元串_C語言 仿JS的eval函數 解析字元串

計算sin(1.57)+cos(if(1>0,0,1))的結果為2.0(三角函數的精度不太好,湊合吧)

其中if(1>0,0,1),if函數接受3個參數,第一個參數是條件,顯然條件成立,傳回第二個參數(表達式)的結果的值,就是0,即計算sin(1.57)+cos(0)

c++ _int64轉字元串_C語言 仿JS的eval函數 解析字元串

注意到:sin在中綴表達式和字尾表達式被替換為了J

cos被替換為了K。

因為注意到單字元的友善性,就用replaceString()函數替換為單字元大寫字元。是以,要注意不要讓使用者輸入大寫字母!

圖中間兩行輸出可以在代碼中注釋掉。

附幾張移植到電腦上的解析字元串效果圖

(畫複雜函數圖像的,例如sin(X)<cos(Y)這種函數的圖像)

c++ _int64轉字元串_C語言 仿JS的eval函數 解析字元串
c++ _int64轉字元串_C語言 仿JS的eval函數 解析字元串

4、思路

這裡我用例子解釋。

假如使用者輸入“-sin(12.57)-cos(-6)”

規定:如果減号“-”出現在符号後面(不包括右括号)或者出現在字元串開頭,就替換為負号“#”,負号是一進制運算符,減号是二進制運算符,是以有必要區分。

故原字元串被替換為“#sin(12.57)-cos(#6)”

由于sin,cos是多字元不好處理,則替換為單字元(這裡我用A-Z),故替換為“#J(12.57)-K(#6)”

接下來隻需要區分是符号還是數字即可。

讀入#是符号

讀入J是符号

讀入(是符号(以上都是轉字尾表達式的标準操作,不再贅述,詳細可見開頭推薦的部落格)

讀入1是數字,不确定是不是一個完整的數字,則存在number裡,

讀入2是數字,将原來的number*10+2。

讀入.表明接下來的數字是小數部分了。

讀入5,number+=pow(10,-1)*5

讀入7,number+=pow(10,-2)*7

當讀入符号或者到字元串末尾時,講存的數字儲存下來,并且清空。

以此類推

這樣子符号和數字就被區分開了,并且轉為字尾表達式了。

接下來就隻需要運作字尾表達式的求值和控制好優先級了。

因為我這個算法的特殊性,以下的奇葩寫法也能計算出正确結果:

sin7

7sin

(7)sin

如有需要,請讀者自行編寫文法分析。

5、源代碼(符合c98标準)

詳細請閱讀代碼中的注釋

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

/**
 * Developed by sandyz987
 * Function 'eval'
 * @param Expression string (chars' length <= MAX_SIZE)
 * @return Answer : double
 * @isError 0:no error  1:wrong number of decimal points  2:can't get top item at an empty stack  3:can't pop item at an empty stack(number of brackets is invalid?)
 *          4:can't get priority   5:too many arguments   6:unexpect character   7:wrong number of arguments   8:math error
 */

#define PI 3.141592653
#define MAX_SIZE 1024
#define MAX_SIGN_NUM 26
#define MIN_NUM 1.0e-7
char *functionName[MAX_SIGN_NUM] = {">=", "<=", "!=", "==", ">", "<", "asin", "acos", "atan","sin", "cos", "tan",  "rand", "deg", "if", "rad", "log", "ln", "exp", "min", "max", "sign", "round", "floor", "abs", "sqrt"};
char nameTran[MAX_SIGN_NUM] = {'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'};
int namePriority[MAX_SIGN_NUM] = {2,2,2,2,2,2,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8};//function's priority
int nameArgNum[MAX_SIGN_NUM] = {2,2,2,2,2,2,1,1,1,1,1,1,2,1,3,1,2,1,1,2,2,1,1,1,1,1};//Number of arguments

char operator[] = {'+','-','*','/','^','!','%','(',')',',','&','|','~','#'};
int priority[] = {3,3,4,4,5,5,5,-4,-5,-1,1,0,5,8};//Operator' priority
int operatorArgNum[] = {2,2,2,2,2,1,2,0,0,1,2,2,1,1};//Number of arguments


//I didn't use the struct to build a stack because pointer can reduce readability.
char operatorS[MAX_SIZE] = {0};//Operator stack
int operatorSTop = -1;
int isError = 0;//0=no error

typedef struct sign{
    int isOperator;//If isOperator == 0 use the num, else use the opera
    double num;
    char opera;
} SIGN;
SIGN signs[MAX_SIZE];int signsSize = 0;//To save the "infix expression" by using "struct SIGN"
SIGN reverseSigns[MAX_SIZE];int reverseSignsSize = 0;//To save the "Postfix Expression"
/*
 * Example:
 * if user input str = "1+2*3"
 * the signs(Stack) store 5 item : [isOperator=0,num=1,opera='0'],[isOperator=1,num=0,opera='+'],[isOperator=0,num=2,opera='0'],[isOperator=1,num=0,opera='*'],[isOperator=0,num=3,opera='0']
 * the reverseSigns(Stack) store 5 item : [isOperator=0,num=2,opera='0'],[isOperator=0,num=3,opera='0'],[isOperator=1,num=0,opera='*'],[isOperator=0,num=1,opera='0'],[isOperator=1,num=0,opera='+']
 */


int getPriority(char c){
    int i;
    for(i = 0; i < sizeof(operator); i++){
        if(operator[i] == c){
            return priority[i];
        }
    }
    for(i = 0; i < sizeof(nameTran); i++){
        if(nameTran[i] == c){
            return namePriority[i];
        }
    }
    isError = 4;
    return 0;
}

void pushSignOpera(char c){
    signs[signsSize].isOperator = 1;
    signs[signsSize].opera = c;
    signsSize++;
}

void pushSignNum(double n){
    signs[signsSize].isOperator = 0;
    signs[signsSize].num = n;
    signsSize++;
}


void pushReverseOpera(char c){
    reverseSigns[reverseSignsSize].isOperator = 1;
    reverseSigns[reverseSignsSize].opera = c;
    reverseSignsSize++;
}

void pushReverseNum(double n){
    reverseSigns[reverseSignsSize].isOperator = 0;
    reverseSigns[reverseSignsSize].num = n;
    reverseSignsSize++;
}


void deleteReverseItem(int pos){
    int i;
    for (i = pos + 1; i < reverseSignsSize; i++) {
        reverseSigns[i-1] = reverseSigns[i];
    }
    reverseSignsSize--;
}

void insertReverseNum(int pos, double n){
    int i;
    for (i = reverseSignsSize - 1; i >= pos; i--) {
        reverseSigns[i+1] = reverseSigns[i];
    }
    reverseSigns[pos].isOperator = 0;
    reverseSigns[pos].num = n;
    reverseSignsSize++;
}

int isNumber(char c){
    return (c>='0'&&c<='9')||(c=='.');
}

int isOperator(char c){
    int flag = 0,i;
    for(i = 0; i<sizeof(operator); i++){
        if (c == operator[i]){
            flag = 1;
        }
    }
    for(i = 0; i<sizeof(nameTran); i++){
        if (c == nameTran[i]){
            flag = 1;
        }
    }
    return flag;
}


void pushOpera(char opera){//Operator stack
    operatorS[++operatorSTop] = opera;
}


int isNotEmptyOperaS(){
    return operatorSTop != -1;
}


char popOpera(){
    return operatorS[operatorSTop--];
}

char getTopOpera(){
    if (operatorSTop != -1){
        return operatorS[operatorSTop];
    } else{
        isError = 2;
        return '0';
    }
}


void replaceString(char s[], int pos, int len, char s1[]){//Replace the s from pos to len with s2
    int i;
    char s2[1000];
    int lenS1 = (int)strlen(s1);
    int lenS = (int)strlen(s);
    int j;
    //copy s to s2 and clear the s
    for (i = 0; i < lenS; i++) {
        s2[i] = s[i];
    }
    memset(s,'0',sizeof(*s));
    for (i = 0; i < pos; ++i) {
        s[i] = s2[i];
    }
    for (i = pos; i < pos + lenS1; i++) {
        s[i] = s1[i - pos];
    }
    j = pos + lenS1;
    for (i = pos + len; i < lenS; i++){
        s[j++] = s2[i];
    }
    s[j] = '0';
}

void tranString(char s[]){//Format string. For example "sin(3.14)+abs(-1)" is format to "J(3.14)+Y(-1)"
    int pos = 0;
    int i;
    while (pos < strlen(s)){
        for (i = 0; i < MAX_SIGN_NUM; i++) {
            if(pos + (int)strlen(functionName[i]) <= (int)strlen(s)){
                char tmp[20];
                memset(tmp,'0',sizeof(tmp));
                strncpy(tmp,s + pos ,strlen(functionName[i]));
                if(strcmp(functionName[i], tmp) == 0){
                    char tmpChar[2] = {'0', '0'};
                    tmpChar[0] = nameTran[i];
                    replaceString(s, pos, (int)strlen(functionName[i]), tmpChar);
                }
            }

        }
        pos++;
    }
    if(s[0] == '-'){//decide whether the '-' is '#'
        char tmpChar[2] = {'#', '0'};
        replaceString(s, 0, 1, tmpChar);
    }
    pos = 1;
    while (pos < strlen(s)){//decide whether the '-' is '#'
        if(isOperator(s[pos - 1]) && s[pos] == '-' && s[pos-1]!=')'){
            char tmpChar[2] = {'#', '0'};
            replaceString(s, pos, 1, tmpChar);
        }
        pos++;
    }
}

int getOperaArgNum(char op){//Get operator's number of arguments.
    int i;
    for (i = 0; i < sizeof(nameTran); ++i) {
        if(nameTran[i] == op){
            return nameArgNum[i];
        }
    }
    for (i = 0; i < sizeof(operator); ++i) {
        if(operator[i] == op){
            return operatorArgNum[i];
        }
    }
    isError = 6;
    return 0;
}

int long fact(int n){//return the number's factor
    if (n < 0)
        return -1;
    if (n > 1)
        return fact(n - 1) * n;
    else
        return n;
}

double calculate(double *n, char op, int num){//Arguments are in *n. op is the operator. num is the number of arguments
    switch (op) {
        case ',':
            return n[num - 1];
        case '#':
            return -n[num - 1];
        case '+':
            return n[num - 1] + n[num - 2];
        case '-':
            return n[num - 1] - n[num - 2];
        case '*':
            return n[num - 1] * n[num - 2];
        case '/':
            return n[num - 2] != 0 ? n[num - 1] / n[num - 2] : (isError = 8, 0);
        case '%':
            return (double)((int)n[num - 1] % (int)n[num - 2]);
        case '^':
            return pow(n[num - 1] , n[num - 2]);
        case '!':
            return fact((int)n[num - 1]);
        case '&':
            return fabs(n[num - 1]) >= MIN_NUM && fabs(n[num - 2]) >= MIN_NUM;
        case '|':
            return fabs(n[num - 1]) >= MIN_NUM || fabs(n[num - 2]) >= MIN_NUM;
        case '~':
            return fabs(n[num - 1]) <= MIN_NUM;
        case 'A':
            return n[num - 1] >= n[num - 2];
        case 'B':
            return n[num - 1] <= n[num - 2];
        case 'C':
            return fabs(n[num - 1] - n[num - 2]) >= MIN_NUM;
        case 'D':
            return fabs(n[num - 1] - n[num - 2]) <= MIN_NUM;
        case 'E':
            return n[num - 1] > n[num - 2];
        case 'F':
            return n[num - 1] < n[num - 2];
        case 'G':
            return n[num - 1] <= 1 && n[num - 1] >= -1 ? asin(n[num - 1]) : (isError = 8, 0);
        case 'H':
            return n[num - 1] <= 1 && n[num - 1] >= -1 ? acos(n[num - 1]) : (isError = 8, 0);
        case 'I':
            return atan(n[num - 1]);
        case 'J':
            return sin(n[num - 1]);
        case 'K':
            return cos(n[num - 1]);
        case 'L':
            return tan(n[num - 1]);
        case 'M':
            return n[num - 1] >= 0 && n[num - 2] >= 0 && n[num - 2] - n[num - 1] >= 1 ? (rand() % ((int)n[num - 2] - (int)n[num - 1]) + 1) + (int)n[num - 1] : (isError = 8, 0);
        case 'N':
            return n[num - 1] / PI * 180.0;
        case 'O':
            return fabs(n[num - 1]) >= MIN_NUM ? n[num - 2] : n[num - 3] ;
        case 'P':
            return n[num - 1] / 180.0 * PI;
        case 'Q':
            return n[num - 1] != 1 && n[num - 1] > 0 && n [num - 2] > 0 ? log(n[num - 2]) / log(n[num - 1]) : (isError = 8, 0);
        case 'R':
            return n[num - 1] > 0 ? log(n[num - 1]) : (isError = 8, 0);
        case 'S':
            return exp(n[num - 1]);
        case 'T':
            return n[num - 1] <= n[num - 2] ? n[num - 1] : n[num - 2];
        case 'U':
            return n[num - 1] <= n[num - 2] ? n[num - 2] : n[num - 1];
        case 'V':
            return n[num - 1] >= 0 ? 1 : -1;
        case 'W':
            return (double)(int)(n[num - 1] + 0.5);
        case 'X':
            return (double)(int)(n[num - 1]);
        case 'Y':
            return n[num - 1] >= 0 ? n[num - 1] : - n[num - 1];
        case 'Z':
            return n[num - 1] >= 0 ? sqrt(n[num - 1]) : (isError = 8, 0) ;
        default://not find the operator
            isError = 6;
            return 0.0f;
    }
}

void calculateOpera(char op, int pos){//Change the reverseSigns(stack) when calculating
    int num = getOperaArgNum(op);
    int i;
    double n[10] = {0};
    int size = 0;
    double ans;

    if (pos >= num){
        for (i = 0; i < num; ++i) {
            if(reverseSigns[pos - 1 - i].isOperator != 1){
                n[size++] = reverseSigns[pos - 1 - i].num;
            } else{
                isError = 7;
                break;
            }
            deleteReverseItem(pos - i);
        }
        deleteReverseItem(pos - i);

        ans = calculate(n, op, num);
        insertReverseNum(pos - num, ans);
    }else {
        isError = 7;
    }
}

double eval(char s[]){
    double number = 0;
    int numberUsed = 0;
    int numberPoint = 0;
    int i;
    operatorSTop = -1;
    signsSize = 0;
    reverseSignsSize = 0;

    srand(0);//set srand!
    isError = 0;
    //tranString(s);   !!!!You must decide whether use "tranString" function here or before eval() execute. Because tranString() use too much time.
    while(*s != '0'){
        if (isNumber(*s)){
            numberUsed = 1;
            if (*s == '.'){
                if (numberPoint != 0){
                    isError = 1;
                }
                numberPoint = 1;
                s++;
                continue;
            }
            if(numberPoint == 0){
                number *= 10.0;
                number += *s - '0';
            }else{
                number += pow(10,-(numberPoint++)) * (*s - '0');
            }
        }
        if (isOperator(*s)){
            if (numberUsed == 1){
                numberUsed = 0;
                pushSignNum(number);
                number = 0;
                numberPoint = 0;
            }
            pushSignOpera(*s);
        }

        s++;
    }
    if(numberUsed != 0){
        pushSignNum(number);
    }
    if(isError){
        return 0.0f;
    }
    //start calculating the sign stack

    for(i = 0; i < signsSize; i++){
        SIGN sign = signs[i];
        if(sign.isOperator != 1){
            //is number
            pushReverseNum(sign.num);
        }else{
            //is operator
            if(sign.opera == '('){
                pushOpera(sign.opera);
            }else if(sign.opera == ')'){
                while(getTopOpera() != '('){
                    if(isNotEmptyOperaS()){
                        pushReverseOpera(popOpera());
                    } else{
                        isError = 3;
                        break;
                    }
                }
                if(isNotEmptyOperaS()){
                    popOpera();
                }
            }else{
                while(isNotEmptyOperaS() && getPriority(getTopOpera()) >= getPriority(sign.opera)){
                    pushReverseOpera(popOpera());
                }
                pushOpera(sign.opera);
            }
        }
    }
    while (isNotEmptyOperaS()){
        char tmp = popOpera();
        if(tmp != '(' && tmp != ')'){
            pushReverseOpera(tmp);
        }
    }

//===========================up --This code block is to test print the "infix expression" and the "Postfix Expression"
//    for(i = 0; i < signsSize; i++){
//        if(!signs[i].isOperator){
//            printf("%f,",signs[i].num);
//        }else{
//            printf("%c,",signs[i].opera);
//        }
//    }
//    printf("n");
//    for(i = 0; i < reverseSignsSize; i++){
//        if(!reverseSigns[i].isOperator){
//            printf("%f,",reverseSigns[i].num);
//        }else{
//            printf("%c,",reverseSigns[i].opera);
//        }
//    }
//    printf("n");
//============================down  --This code block is to test print the "infix expression" and the "Postfix Expression"

    //start calculate the expression by reverse (Postfix) expression
    while(!isError){
        int pos = -1;
        for (i = 0; i < reverseSignsSize; i++) {
            if(reverseSigns[i].isOperator == 1){
                pos = i;
                break;
            }
        }
        if(pos == -1){
            break;
        }else{
            calculateOpera(reverseSigns[i].opera, pos);
        }
    }


    if(isError){
        return 0.0f;
    }
    if(reverseSignsSize != 1){
        isError = 5;
        return 0.0f;
    } else{
        return reverseSigns[0].num;
    }

}

int main(){
    char s[100];
    scanf("%s",s);
    tranString(s);
    printf("%f",eval(s));
    return 0;
}

           

原文連結:C語言 科學電腦 仿JS的eval函數 解析字元串 字尾表達式

繼續閱讀