C# 计算一串字符串算法
工作中遇到一个小问题,就是要做一个类似excel那种的公式的东西,就是A0+A1*B0那样的公式,然后得出结果。
首先,分析。
这不是计算器,计算器要比这个简易,计算器是所按即所得,即你点击+-之类的按钮时候,你的数字已经确认了,你所要做的只是转换一下string和decimal而已。
比如1+2*(2+4)/4-1
如果再算上幂运算,我打不出来幂运算的符号,尴尬。
我们可以这么写,比如,遇到的第一个数字是1,那么定义一个变量firnum=1 第一个符号是+,定义一个变量 mark=+,第二个数字是2,顶一个一个变量secnum=2,第二个符号是*,这时候进行判断,因为*/比加减的运算级别高,要先算乘除,所以,这里1和+要缓存起来,继续往下走,然后计算(),得出()内的数字是6,这时候先运算2*6,然后遇到/,计算12/4,再往后走,遇到-,这时候+-的运算级别一样,则开始运算之前的1和+,然后依次运算,最后得出结果。
怎么说呢,我们姑且认为这是一个方法吧,姑且认为,这么辛苦了,写了这么多代码,能进行四则运算,还挺正确,也不容易,没有功劳也有苦劳。
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 = ; i < str.Length; i++)
{
char c = str[i]; //判断如果是数字和.
if (( < c && c < ) || 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 = ;
for (int j = i + ; j < str.Length; j++)
{
var k = str[j];
if (k == '(')
{
temp++;
}
else if (k == ')')
{
temp--;
} if (temp == )
{
temp = j - i - ;
}
} var kh = CalcRet(str.Substring(i + , temp));
if (lastMark != '!')
{ if (secStr != "")
{
tempStr = kh.ToString();
}
else
{
secNum = kh;
secStr = kh.ToString();
}
}
else
{
if (i == )
{
firNum = kh;
}
else
{
secNum = kh;
secStr = kh.ToString();
}
} i += temp + ;
} }
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;
}
}
对,就是这种写法。
在我看来,这么写的代码,真的只是一堆垃圾,我不是针对谁,我是说写成这样的逻辑,它就是垃圾,连没毕业的大学生都不如。
比如,如果加幂运算如果加mod怎么办,我就问你怎么办?
继续判断?
写不死你!
然后,我们可以换个思路。
所谓运算,不过是两个数字和一个符号之间故事,抱歉,我觉得一对一那种男女关系不适用于这里,开个玩笑,呵呵!强行尬聊~
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 = ; i < str.Length; i++)
{
char c = str[i]; //判断如果是数字和.
if (( < c && c < ) || c == '.')
{
strNum += c; if (i == str.Length - )
{
if (!string.IsNullOrEmpty(strNum))
{
decimal.TryParse(strNum, out num);
numList.Add(num);
strNum = "";
}
}
continue;
}
else if (c == '(')
{
int temp = ;
for (int j = i + ; j < str.Length; j++)
{
var k = str[j];
if (k == '(')
{
temp++;
}
else if (k == ')')
{
temp--;
} if (temp == )
{
temp = j - i - ;
}
} strNum = str.Substring(i + , temp);
numList.Add(CalcStr(strNum));
strNum = "";
i += temp + ;
}
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 = ; t < tempOrder.Count; t++)
{
for (int i = ; i < operList.Count; i++)
{
if (operList[i].PrioRity == tempOrder[t])
{
numList[i] = operList[i].OperationResult(numList[i], numList[i + ]);
numList.RemoveAt(i + );
operList.RemoveAt(i);
i--;
}
}
} if (numList.Count == ) return numList[]; return 0m;
} public class Operation
{
protected int priority = ;
/// <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 ;
}
} public override decimal OperationResult(decimal a, decimal b)
{
return a * b;
}
} public class DivOperation : Operation
{
public override int PrioRity
{
get
{
return ;
}
}
public override decimal OperationResult(decimal a, decimal b)
{
return a / b;
}
} public class ModOperation : Operation
{
public override int PrioRity
{
get
{
return ;
}
}
public override decimal OperationResult(decimal a, decimal b)
{
return a % b;
}
} }
PrioRity这个是优先级,我比较懒,就从99往上了
但是这样真的很明了啊,而且可以随时添加新的算法,简直了 我想说的是,能简便的尽量简便,能通运的尽量通用,自己看的舒服,别人看的也舒服,是不是~
C# 计算一串字符串算法的更多相关文章
- iOS:使用莱文斯坦距离算法计算两串字符串的相似度
Levenshtein:莱文斯坦距离 Levenshtein的经典算法,参考http://en.wikipedia.org/wiki/Levenshtein_distance的伪代码实现的,同时参考了 ...
- MD5算法【计算文件和字符串的MD5值】
1. MD5算法是一种散列(hash)算法(摘要算法,指纹算法),不是一种加密算法(易错).任何长度的任意内容都可以用MD5计算出散列值.MD5的前身:MD2.MD3.MD4.介绍工具:CalcMD5 ...
- Levenshtein Distance + LCS 算法计算两个字符串的相似度
//LD最短编辑路径算法 public static int LevenshteinDistance(string source, string target) { int cell = source ...
- boost字符串算法
boost::algorithm简介 2007-12-08 16:59 boost::algorithm提供了很多字符串算法,包括: 大小写转换: 去除无效字符: 谓词: 查找: 删除/替换: 切割: ...
- 基础数据结构-串-KMP算法
KMP算法用于模式串字符匹配,因为没有提前预习,上课时听得云里雾里,后来回去看了一晚上,翻了一些网上的讲解才理解了.我简单讲一下,我们在一串字符串A里搜索匹配另一段字符串B时,思路最简单方法的就是从第 ...
- 利用编辑距离(Edit Distance)计算两个字符串的相似度
利用编辑距离(Edit Distance)计算两个字符串的相似度 编辑距离(Edit Distance),又称Levenshtein距离,是指两个字串之间,由一个转成另一个所需的最少编辑操作次数.许可 ...
- python-Levenshtein几个计算字串相似度的函数解析
linux环境下,没有首先安装python_Levenshtein,用法如下: 重点介绍几个该包中的几个计算字串相似度的几个函数实现. 1. Levenshtein.hamming(str1, str ...
- 【字符串算法2】浅谈Manacher算法
[字符串算法1] 字符串Hash(优雅的暴力) [字符串算法2]Manacher算法 [字符串算法3]KMP算法 这里将讲述 字符串算法2:Manacher算法 问题:给出字符串S(限制见后)求出最 ...
- 【字符串算法3】浅谈KMP算法
[字符串算法1] 字符串Hash(优雅的暴力) [字符串算法2]Manacher算法 [字符串算法3]KMP算法 这里将讲述 [字符串算法3]KMP算法 Part1 理解KMP的精髓和思想 其实KM ...
随机推荐
- 【3Sum】cpp
题目: Given an array S of n integers, are there elements a, b, c in S such that a + b + c = 0? Find al ...
- [netty4][netty-common]Future与Promise分析
接口与类结构体系 -- [I]java.util.concurrent.Future<V> ---- [I]io.netty.util.concurrent.Future<V> ...
- Python+Selenium练习篇之9-清除文本方法
在前面的基础篇的最后一篇,我们用到了输入字符和点击按钮这样的操作.用send_keys()来输入字符串到文本输入框这样的页面元素,用click()来点击页面上支持点击的元素.有时候,我们需要清除一个文 ...
- ACM-ICPC 2018 南京赛区网络预赛
轻轻松松也能拿到区域赛名额,CCPC真的好难 An Olympian Math Problem 问答 只看题面 54.76% 1000ms 65536K Alice, a student of g ...
- File IO(NIO.2):路径类 和 路径操作
路径类 Java SE 7版本中引入的Path类是java.nio.file包的主要入口点之一.如果您的应用程序使用文件I / O,您将需要了解此类的强大功能. 版本注意:如果您有使用java.io. ...
- hdu 4185 二分图最大匹配
Oil Skimming Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Tota ...
- 环境说明与HelloWorld
本人采用的是ExtJs4.2版本,采用WebStorm作为IDE开发工具 目录说明 builds:压缩后的ExtJs代码 docs:文档 examples:官方示例 locale:多国语言的资源文件 ...
- eclipse快捷键补全
Eclipse中 补全快捷键 默认Alt+/ 但是每个人习惯有所不同 我需要来修改自己熟悉的快捷键 windows->preferences->General->keys将Conte ...
- BZOJ 3150 [Ctsc2013]猴子 ——期望DP 高斯消元
一堆牌的期望等于每张牌的期望值和. 考虑三个人的游戏即可得到. 然后每张牌遇到另外一张的概率相同,然后就可以列方程求解了. #include <cmath> #include <cs ...
- BZOJ 3462 DZY Loves Math II ——动态规划 组合数
好题. 首先发现$p$是互质的数. 然后我们要求$\sum_{i=1}^{k} pi*xi=n$的方案数. 然后由于$p$不相同,可以而$S$比较小,都是$S$的质因数 可以考虑围绕$S$进行动态规划 ...