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 ...
随机推荐
- Dataflow编程模型和spark streaming结合
Dataflow编程模型和spark streaming结合 主要介绍一下Dataflow编程模型的基本思想,后面再简单比较一下Spark streaming的编程模型 == 是什么 == 为用户提 ...
- poj 2251 Dungeon Master 3维bfs(水水)
Dungeon Master Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 21230 Accepted: 8261 D ...
- hdu 4185 二分图最大匹配
Oil Skimming Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Tota ...
- git 远程仓库回滚
git branch backup #创建备份分支 git push origin backup:backup #push到远程 git reset --hard commit_id #本地分支回滚 ...
- Ambari API 验证方式
文章作者:luxianghao 文章来源:http://www.cnblogs.com/luxianghao/p/6123010.html 转载请注明,谢谢合作. 免责声明:文章内容仅代表个人观点, ...
- mysql错误之2014
error:2014 Commands out of sync; you can't run this command now 这个错误号我也真是醉了. 一直纠结于为什么存储过程执行完,commit操 ...
- Linux 程序编译过程的来龙去脉
大家肯定都知道计算机程序设计语言通常分为机器语言.汇编语言和高级语言三类.高级语言需要通过翻译成机器语言才能执行,而翻译的方式分为两种,一种是编译型,另一种是解释型,因此我们基本上将高级语言分为两大类 ...
- 荔枝丹(litchi)
荔枝丹(litchi) 题目描述 绛雪艳浮红锦烂,玉壶光莹水晶寒. 高名已许传新曲,芳味曾经荐大官. 乌府日长霜署静,几株斜覆石栏杆. --明·陈辉<荔枝> 荔枝(丹),拼音为lizhid ...
- Python基础教程笔记 第一章
/ 表示整除,当导入_future_模块中的version时,/ 表示正常的的除法, 此时可用//表示整除,不论数字是整型还是浮点型,都可以用//表示整除. ** 表示幂次方 例如 2**3 ...
- python获取目录下文件夹名称
path = '/opt' dirs = os.listdir(path) for dir in dirs: print dir