利⽤正则表达式处理四则运算
regex匹配不久之前我写过⼀篇的⽂章,其中验证部分我使⽤了正则表达式,但计算部分还是依靠基本流程处理的。后来想了想,计算是否也能使⽤正则表达式呢?再做⼀个逻辑表达式计算就没太⼤意思了,这次咱来试试四则运算。
我的基本思路是先乘除后加减,先运算式⼦中简单的乘除法例如:“2*5”,但“2*(3+4)”不必处理先,咱们先解决简单的。然后处理简单的加减法,例如"3+4",同样涉及括号的先不处理。最后去括号,但是只去⽆⽤的括号,例如“(3)”,这样⼀来连续的式⼦(例如:2*8+9/3)总是能化
成“(19)”,这样最终被去掉括号变成“19”再与其他部分做运算。只需要重复以上的部分最终就能完成运算了。但其实其中还漏了⼀步,那就是做完乘除运算之后还有⼀些应该在加减之前先运算的没有被保护起来,例如:“(1/2+2*3+1)/2+2*3”做完乘除运算之后式⼦为“(0.5+6+1)/2+6”如果现在执⾏加减运算,由于“2+6”能够被匹配,所以会被先计算那样就错了。所以先要把“(0.5+6+1)/2”作为⼀个整体保护起来,我第⼀想到的办法就是加括号即“((0.5+6+1)/2)+6”,这样就不会被匹配了。但是⼜有⼀个问题:如何确定哪些位置需要加括号呢?⾸先要加括号的地⽅⼀定是相乘除的两个部分中刚好有⼀个是带括号的式⼦另⼀个却是普通的数字,不然它⼀定会在上⼀步中被消灭掉,或者两个部分都带有括号意味着他们不需要先计算,不需要理会,其次他们没有被括号包围。如果⽤正则表达式来匹配似乎⽐较困难,
原因是有括号的那⼀部分中可能有不确定的内层括号,不能简单的⽤“\(\S+\)”,否则就会出⼤乱⼦了。那怎么办呢?想了很久,发现这并不是⼀种好的解决⽅法,我开始换⼀种思路,既然添加括号不⽅便,那么能不能不加括号却不会造成错误呢?答案是有!例如在“(0.5+6+1)/2+6”中,先计算括号内的简单加减式⽽不管外⾯的加减式,也就是在加减运算的正则表达式中⾸尾加上括号即可。这样的话最终会照成算式⽆法完成,原因是没有括号的加减法始终不能被匹配。要解决这个问题我们还需要加多⼀个正在表达式来做最后的⼯作,这个表达式必须能匹配简单加减式,即“((\d+(\.(\d+))?)((\+|\-)(\d+(\.(\d+))?))*)”,且算式匹配的值刚好等于算式本⾝,否则继续循环下去。这样真的没有问题吗?下⾯我把源代码贴出来,供⼤家参考:
string Calculate(Match match)
{
string result = match.Value;
result = result.Replace("(", "");
result = result.Replace(")", "");
char Operator = '+';
double X = 0;
double Y = 0;
int index = 0;
for (int i = 0; i < result.Length; i++)
{
if (result[i] == '+' || result[i] == '-' || result[i] == '*' || result[i] == '/' || i == result.Length - 1)
{
if (i == result.Length - 1)
{
Y = Convert.ToDouble(result.Substring(index, i - index + 1));
}
else
{
Y = Convert.ToDouble(result.Substring(index, i - index));
}
index = i + 1;
switch (Operator)
{
case'+':
X = X + Y;
break;
case'-':
X = X - Y;
break;
case'*':
X = X * Y;
break;
case'/':
X = X / Y;
break;
}
Operator = result[i];
}
}
return X.ToString();
}string Eliminate(Match match)
{
return match.Value.Substring(1, match.Value.Length - 2);
}
internal string Calculation(string expression)
{
Regex regexMultiplication = new Regex(@"((\d+(\.(\d+))?)((\*|\/)(\d+(\.(\d+))?))*)");
Regex regexAddition = new Regex(@"\(((\d+(\.(\d+))?)((\+|\-)(\d+(\.(\d+))?))*)\)");
Regex regexEliminate = new Regex(@"\(\d+(\.(\d+))?\)");
Regex regexComplete = new Regex(@"((\d+(\.(\d+))?)((\+|\-)(\d+(\.(\d+))?))*)");
while (true)
{
expression = regexMultiplication.Replace(expression, Calculate);
expression = regexAddition.Replace(expression, Calculate);
expression = regexEliminate.Replace(expression, Eliminate);
if (regexComplete.Match(expression).Value == expression)
{
return expression = regexComplete.Replace(expression, Calculate);
}
}
}
事实上以上代码中仍然存在不能⽀持包含负数的运算的问题,例如“5+(2-9)”,“(2-9)”会被先计算成"(-7)",它不能被任何⼀个正则表达式匹配。为了解决这个问题,我修改了源代码,在原来匹配数字的地⽅的前⾯加上"[-+]?",这样数字前就能够包含正负号了。我也完全修改了简单计算的算法并将加减运算和乘除运算分开(原因是包含正负号之后处理逻辑不同),具体如下:
class Arithmetic
{
Regex regexMultiplicationAndDivision = new Regex(@"(([-+]?\d+(\.(\d+))?)((\*|\/)([-+]?\d+(\.(\d+))?))+)");
Regex regexAdditionAndSubtraction = new Regex(@"\((([-+]?\d+(\.(\d+))?)((\+|\-)([-+]?\d+(\.(\d+))?))+)\)");
Regex regexEliminate = new Regex(@"\([-+]?\d+(\.(\d+))?\)");
Regex regexComplete = new Regex(@"(([-+]?\d+(\.(\d+))?)((\+|\-)([-+]?\d+(\.(\d+))?))*)");
Regex regexError = new Regex(@"\)\(|\)(\d+(\.(\d+))?)|(\d+(\.(\d+))?)\(");
internal string Calculation(string expression)
{
if (regexError.IsMatch(expression))
{
throw new Exception();
}
while (true)
{
int iNotMatch = 0;
if (regexMultiplicationAndDivision.IsMatch(expression))
{
expression = regexMultiplicationAndDivision.Replace(expression, MultiplicationAndDivision);
}
else
{
iNotMatch++;
}
if (regexAdditionAndSubtraction.IsMatch(expression))
{
expression = regexAdditionAndSubtraction.Replace(expression, AdditionAndSubtraction);
}
else
{
iNotMatch++;
}
if (regexEliminate.IsMatch(expression))
{
expression = regexEliminate.Replace(expression, Eliminate);
}
else
{
iNotMatch++;
}
if (regexComplete.Match(expression).Value == expression)
{
return Convert.ToDouble(regexComplete.Replace(expression, AdditionAndSubtraction)).ToString();
}
if (iNotMatch == 3)
{
throw new Exception();
}
}
}
string MultiplicationAndDivision(Match match)
{
string text = match.Value;
bool isPositive = true;
foreach (char c in text)
{
if (c == '-')
{
isPositive = !isPositive;
}
}
text = text.Replace("*+", "*");
text = text.Replace("*-", "*");
text = text.Replace("/+", "/");
text = text.Replace("/-", "/");
text = text.Replace("*", ",*");
text = text.Replace("/", ",/");
string[] numbers = text.Split(',');
double result = Convert.ToDouble(numbers[0]) >= 0 ? Convert.ToDouble(numbers[0]) : (-Convert.ToDouble(numbers[0]));
for (int i = 1; i < numbers.Length;i++ )
{
if (numbers[i] != "")
{
switch (numbers[i][0])
{
case'*':
result *= Convert.ToDouble(numbers[i].Substring(1, numbers[i].Length - 1));
break;
case'/':
result /= Convert.ToDouble(numbers[i].Substring(1, numbers[i].Length - 1));
break;
}
}
}
if (isPositive == false)
{
result = -result;
}
return result >= 0 ? ("+" + result.ToString()) : result.ToString(); }
string AdditionAndSubtraction(Match match)
{
string text = match.Value;
text = text.Replace("(", "");
text = text.Replace(")", "");
text = text.Replace("++", "+");
text = text.Replace("+-", "-");
text = text.Replace("-+", "-");
text = text.Replace("--", "+");
text = text.Replace("+", ",+");
text = text.Replace("-", ",-");
string[] numbers = text.Split(',');
double result = 0;
foreach (string number in numbers)
{
if (number != "")
{
result += Convert.ToDouble(number);
}
}
return result >= 0 ? ("+" + result.ToString()) : result.ToString(); }
string Eliminate(Match match)
{
return match.Value.Substring(1, match.Value.Length - 2);
}
}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论