C# 計算一串字符串算法

工做中遇到一個小問題,就是要作一個相似excel那種的公式的東西,就是A0+A1*B0那樣的公式,而後得出結果。算法

首先,分析。緩存

這不是計算器,計算器要比這個簡易,計算器是所按即所得,即你點擊+-之類的按鈕時候,你的數字已經確認了,你所要作的只是轉換一下string和decimal而已。ide

好比1+2*(2+4)/4-1函數

若是再算上冪運算,我打不出來冪運算的符號,尷尬。spa

咱們能夠這麼寫,好比,遇到的第一個數字是1,那麼定義一個變量firnum=1  第一個符號是+,定義一個變量 mark=+,第二個數字是2,頂一個一個變量secnum=2,第二個符號是*,這時候進行判斷,由於*/比加減的運算級別高,要先算乘除,因此,這裏1和+要緩存起來,繼續往下走,而後計算(),得出()內的數字是6,這時候先運算2*6,而後遇到/,計算12/4,再日後走,遇到-,這時候+-的運算級別同樣,則開始運算以前的1和+,而後依次運算,最後得出結果。excel

怎麼說呢,咱們姑且認爲這是一個方法吧,姑且認爲,這麼辛苦了,寫了這麼多代碼,能進行四則運算,還挺正確,也不容易,沒有功勞也有苦勞。code

        public decimal CalcRet(string str)
        {
            //第一個數字
            string firStr = string.Empty;
            decimal firNum = 0m;

            //第二個數字;
            string secStr = string.Empty;
            decimal secNum = 0m;

            //temp數字
            string tempStr = string.Empty;

            //當前計算符號
            char curMark = '!';
            //結果
            decimal result = 0m;

            //上一個符號
            char lastMark = '!';

            for (int i = 0; i < str.Length; i++)
            {
                char c = str[i];

                //判斷若是是數字和.
                if ((47 < c && c < 58) || c == '.')
                {
                    //除卻第一次是第一個數字須要轉換,之後都是第一個和第二個進行累加
                    if (curMark == '!')
                    {
                        firStr += c;
                    }
                    else
                    {
                        if (curMark == '+' || curMark == '-')
                        {
                            secStr += c;
                        }
                        else if (curMark == '*' || curMark == '/')
                        {
                            if (lastMark == '+' || lastMark == '-')
                            {
                                tempStr += c;
                            }
                            else
                            {
                                secStr += c;
                            }
                        }
                    }

                    continue;
                }

                if (firStr != "")
                {
                    decimal.TryParse(firStr, out firNum);
                    firStr = "";
                }

                if (c == '+' || c == '-' || c == '*' || c == '/')
                {
                    switch (curMark)
                    {
                        case '+':
                            if (secStr != "" && tempStr != "")
                            {
                                secNum = OperCalc(curMark, secNum, tempStr);
                                firNum = firNum + secNum;
                                secStr = "";
                                tempStr = "";
                            }

                            if (c == '*' || c == '/')
                            {
                                lastMark = curMark;
                                curMark = c;
                                break;
                            }


                            if (secStr == "") continue;

                            firNum = OperCalc(curMark, firNum, secStr);
                            curMark = c;
                            lastMark = c;
                            secStr = "";
                            break;
                        case '-':
                            if (secStr != "" && tempStr != "")
                            {
                                secNum = OperCalc(curMark, secNum, tempStr);
                                firNum = firNum - secNum;
                                secStr = "";
                                tempStr = "";
                            }

                            if (c == '*' || c == '/')
                            {
                                lastMark = curMark;
                                curMark = c;
                                break;
                            }


                            if (secStr == "") continue;

                            firNum = OperCalc(curMark, firNum, secStr);
                            curMark = c;
                            lastMark = c;
                            secStr = "";
                            break;
                        case '*':

                            if (lastMark != '!' && tempStr != "")
                            {
                                secNum = OperCalc(curMark, secStr, tempStr);
                                secStr = secNum.ToString();
                                tempStr = "";

                            }
                            else
                            {
                                firNum = OperCalc(curMark, firNum, secStr);
                                secStr = "";
                                curMark = c;
                                break;
                            }

                            if (c == '+' || c == '-')
                            {
                                if (lastMark != '!')
                                {
                                    firNum = OperCalc(lastMark, firNum, secNum);
                                    secStr = "";
                                    tempStr = "";
                                }
                            }

                            curMark = c;
                            break;
                        case '/':

                            if (lastMark != '!' && tempStr != "")
                            {
                                secNum = OperCalc(curMark, secStr, tempStr);
                                secStr = secNum.ToString();
                                tempStr = "";
                            }
                            else
                            {
                                firNum = OperCalc(curMark, firNum, secStr);
                                secStr = "";
                                curMark = c;
                                break;
                            }

                            if (c == '+' || c == '-')
                            {
                                if (lastMark != '!')
                                {
                                    firNum = OperCalc(lastMark, firNum, secNum);
                                    secStr = "";
                                    tempStr = "";
                                }
                            }

                            curMark = c;
                            break;
                        case '(':
                            break;
                        case ')':
                            break;
                        default:
                            curMark = c;
                            if (c == '+' || c == '-')
                                lastMark = c;
                            break;
                    }
                }
                else if (c == '(')
                {
                    int temp = 1;
                    for (int j = i + 1; j < str.Length; j++)
                    {
                        var k = str[j];
                        if (k == '(')
                        {
                            temp++;
                        }
                        else if (k == ')')
                        {
                            temp--;
                        }

                        if (temp == 0)
                        {
                            temp = j - i - 1;
                        }
                    }

                    var kh = CalcRet(str.Substring(i + 1, temp));
                    if (lastMark != '!')
                    {

                        if (secStr != "")
                        {
                            tempStr = kh.ToString();
                        }
                        else
                        {
                            secNum = kh;
                            secStr = kh.ToString();
                        }
                    }
                    else
                    {
                        if (i == 0)
                        {
                            firNum = kh;
                        }
                        else
                        {
                            secNum = kh;
                            secStr = kh.ToString();
                        }
                    }

                    i += temp + 1;
                }


            }
            if (tempStr != "")
            {
                secNum = OperCalc(curMark, secStr, tempStr);
                secStr = secNum.ToString();
                result = OperCalc(lastMark, firNum, secStr);
            }
            else
            {
                result = OperCalc(curMark, firNum, secStr);
            }
            return result;

        }

        decimal OperCalc(char mark, string fir, string sec)
        {
            decimal a, b;
            decimal.TryParse(fir, out a);
            decimal.TryParse(sec, out b);
            switch (mark)
            {
                case '+':
                    return a + b;
                case '-':
                    return a - b;
                case '*':
                    return a * b;
                case '/':
                    return a / b;
                default:
                    return 0m;
            }
        }

        decimal OperCalc(char mark, decimal fir, string sec)
        {
            decimal b;
            decimal.TryParse(sec, out b);
            switch (mark)
            {
                case '+':
                    return fir + b;
                case '-':
                    return fir - b;
                case '*':
                    return fir * b;
                case '/':
                    return fir / b;
                default:
                    return 0m;
            }
        }

        decimal OperCalc(char mark, decimal fir, decimal sec)
        {
            switch (mark)
            {
                case '+':
                    return fir + sec;
                case '-':
                    return fir - sec;
                case '*':
                    return fir * sec;
                case '/':
                    return fir / sec;
                default:
                    return 0m;
            }
        }

對,就是這種寫法。blog

在我看來,這麼寫的代碼,真的只是一堆垃圾,我不是針對誰,我是說寫成這樣的邏輯,它就是垃圾,連沒畢業的大學生都不如。遞歸

好比,若是加冪運算若是加mod怎麼辦,我就問你怎麼辦?ip

繼續判斷?

寫不死你!

而後,咱們能夠換個思路。

所謂運算,不過是兩個數字和一個符號之間故事,抱歉,我以爲一對一那種男女關係不適用於這裏,開個玩笑,呵呵!強行尬聊~

1+2  是1,2 兩個數字和+之間的運算

1+2+3*(4+5),是12345數字和四個符號進行的運算,至於括號,咱們是否是能夠把括號當成一個遞歸?就是4+5當成一個遞歸,調用同一個函數,返回一個結果就好了

也就是說,數字永遠比符號多一個

咱們是否是能夠這麼想。

list1 ={1,2,3,4,5}

list2={+,+,*,(+)}

第一次運算後

list1 ={1,2,3,9}

list2={+,+,*}

按照優先級,咱們可先計算*

獲得

list1 ={1,2,3,9}{1,2,27}

list2={+,+,*}{+,+}

刪掉*和最後的9,同時刪掉一個符號和一個數字,獲得

list1 ={1,2,27}

list2={+,+}

繼續

list1 ={3,27}

list2={+}

再繼續

list1 ={30}

list2={}

最後就剩下一個數字,好,計算完畢

public class CalcOperation
    {
        /// <summary>
        /// 計算字符串解析表達式 1+2(2*(3+4))
        /// </summary>
        /// <param name="str">傳入的字符串</param>
        /// <returns>計算獲得的結果</returns>
        public decimal CalcStr(string str)
        {
            decimal num = 0m;
            //數字集合
            List<decimal> numList = new List<decimal>();
            //操做符集合
            List<Operation> operList = new List<Operation>();
            string strNum = "";
            for (int i = 0; i < str.Length; i++)
            {
                char c = str[i];

                //判斷若是是數字和.
                if ((47 < c && c < 58) || c == '.')
                {
                    strNum += c;

                    if (i == str.Length - 1)
                    {
                        if (!string.IsNullOrEmpty(strNum))
                        {
                            decimal.TryParse(strNum, out num);
                            numList.Add(num);
                            strNum = "";
                        }
                    }
                    continue;
                }
                else if (c == '(')
                {
                    int temp = 1;
                    for (int j = i + 1; j < str.Length; j++)
                    {
                        var k = str[j];
                        if (k == '(')
                        {
                            temp++;
                        }
                        else if (k == ')')
                        {
                            temp--;
                        }

                        if (temp == 0)
                        {
                            temp = j - i - 1;
                        }
                    }

                    strNum = str.Substring(i + 1, temp);
                    numList.Add(CalcStr(strNum));
                    strNum = "";
                    i += temp + 1;
                }
                else
                {
                    if (!string.IsNullOrEmpty(strNum))
                    {
                        decimal.TryParse(strNum, out num);
                        numList.Add(num);
                        strNum = "";
                    }

                    if (c == '+')
                    {
                        operList.Add(new AddOperation());
                    }
                    else if (c == '-')
                    {
                        operList.Add(new SubOperation());
                    }
                    else if (c == '*')
                    {
                        operList.Add(new MultipOperation());
                    }
                    else if (c == '/')
                    {
                        operList.Add(new DivOperation());
                    }
                    else if (c == '%')
                    {
                        operList.Add(new ModOperation());
                    }
                    else
                    {
                        operList.Add(null);
                    }
                }
            }

            List<int> tempOrder = new List<int>();
            operList.ForEach(w =>
            {
                if (!tempOrder.Contains(w.PrioRity))
                {
                    tempOrder.Add(w.PrioRity);
                }

            });

            tempOrder.Sort();
            for (int t = 0; t < tempOrder.Count; t++)
            {
                for (int i = 0; i < operList.Count; i++)
                {
                    if (operList[i].PrioRity == tempOrder[t])
                    {
                        numList[i] = operList[i].OperationResult(numList[i], numList[i + 1]);
                        numList.RemoveAt(i + 1);
                        operList.RemoveAt(i);
                        i--;
                    }
                }
            }

            if (numList.Count == 1) return numList[0];

            return 0m;
        }

        public class Operation
        {
            protected int priority = 99;
            /// <summary>
            /// 優先級
            /// </summary>
            public virtual int PrioRity
            {
                get
                {
                    return priority;
                }
                set
                {
                    priority = value;
                }
            }

            public virtual decimal OperationResult(decimal a, decimal b)
            {
                return 0m;
            }
        }

        public class AddOperation : Operation
        {
            public override decimal OperationResult(decimal a, decimal b)
            {
                return a + b;
            }
        }

        public class SubOperation : Operation
        {
            public override decimal OperationResult(decimal a, decimal b)
            {
                return a - b;
            }
        }

        public class MultipOperation : Operation
        {
            public override int PrioRity
            {
                get
                {
                    return 98;
                }
            }

            public override decimal OperationResult(decimal a, decimal b)
            {
                return a * b;
            }
        }

        public class DivOperation : Operation
        {
            public override int PrioRity
            {
                get
                {
                    return 98;
                }
            }
            public override decimal OperationResult(decimal a, decimal b)
            {
                return a / b;
            }
        }

        public class ModOperation : Operation
        {
            public override int PrioRity
            {
                get
                {
                    return 97;
                }
            }
            public override decimal OperationResult(decimal a, decimal b)
            {
                return a % b;
            }
        }

    }
PrioRity這個是優先級,我比較懶,就從99往上了可是這樣真的很明瞭啊,並且能夠隨時添加新的算法,簡直了我想說的是,能簡便的儘可能簡便,能通運的儘可能通用,本身看的舒服,別人看的也舒服,是否是~
相關文章
相關標籤/搜索