编码技巧 --- 如何实现字符串运算表达式的计算

这篇具有很好参考价值的文章主要介绍了编码技巧 --- 如何实现字符串运算表达式的计算。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

引言

最近做一个配置的功能,需求是该配置项跟另一个整形配置项关联,具有一定的函数关系,例如有一个配置项是值为 N ,则另一配置 F 项满足函数关系\(F=2/(N+1)\)。这个函数关系是客户手动输入,只需要简单的四则运算,所以我们要做的就是判断四则运算表达式是否有效,且给定 N 的值,算出表达式的值。

如何快速判断一个四则运算公式字符串是否符合规则,且根据给定值计算出该公式的值?

双栈实现

实际上编译器就是利用了双栈实现了的表达式求值,其中一个栈用来保存操作数,另一个栈用来保存运算符。

从左向右遍历表达式,当遇到数字时,就将其直接压入操作数栈;当遇到运算符时,就将其与运算符栈的栈顶元素比较。

如果遇到的运算符比运算符栈顶的元素的优先级高,就将这个运算符压入栈;

如果遇到的运算符比运算符栈顶的元素的优先级低或两者相同,就从运算符栈顶取出运算符,在从操作数栈顶取两个操作数,然后进行计算,并把计算的得到的结果压入操作数栈,继续比较这个运算符与运算符栈顶的元素;

下图表示一个简单四则运算表达式 3+5*8-6的计算过程:

代码实现可以大概简化可以分为以下步骤:

  1. 定义运算符栈 operatorStack 和操作数栈 operandStack

  2. 从左至右扫描表达式,遇到操作数时,直接将其推入操作数栈 operandStack

  3. 遇到运算符时,比较其与运算符栈顶部运算符的优先级:

    • 如果该运算符的优先级高于或等于运算符栈顶部运算符,则将该运算符直接入栈 operatorStack

    • 如果该运算符的优先级低于运算符栈顶部运算符,则将运算符栈顶部的运算符出栈,从操作数栈中弹出两个操作数,计算结果后再入栈 operandStack ,重复此步骤直到运算符栈为空或遇到优先级高于或等于该运算符的栈顶运算符为止。

  4. 遇到括号时:

    • 如果是左括号“(”,则直接入栈 operatorStack

    • 如果是右括号“)”,则将运算符栈栈顶的运算符出栈,从操作数栈中弹出两个操作数计算结果,重复此步骤直到遇到左括号为止,并将这一对括号从运算符栈中移除。

  5. 重复步骤3和4,直到表达式的最右端。

  6. 将运算符栈中剩余的所有运算符依次出栈,从操作数栈中弹出两个操作数,计算结果后入栈operandStack。

  7. 操作数栈最终只剩一个操作数,这就是表达式的计算结果。

具体实现代码如下:

class ExpressionEvaluator
{
    static Dictionary<char, int> PrecedenceDic = new Dictionary<char, int> {
            {'+', 1}, {'-', 1}, {'*', 2}, {'/', 2}, {'^', 3}
        };

    static Dictionary<char, Func<int, int, int>> OperatorsDic = new Dictionary<char, Func<int, int, int>> {
            {'+', (a, b) => a + b },
            {'-', (a, b) => a - b },
            {'*', (a, b) => a * b },
            {'/', (a, b) => a / b },
            {'^', (a, b) => (int)Math.Pow(a, b)}
        };

    public static bool EvaluateExpression(string expression, out double result)
    {
        result = 0;
        try
        {
            // 使用正则表达式验证四则运算表达式的有效性
            string pattern = @"^[-+*/^() x\d\s]+$";

            if (!Regex.IsMatch(expression, pattern))
            {
                return false;
            }
            //操作符栈
            Stack<char> operatorStack = new Stack<char>();
            //操作数栈
            Stack<int> operandStack = new Stack<int>();

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

                if (c == ' ') continue;

                if (char.IsDigit(c))
                {
                    //获取操作数
                    int operand = 0;
                    while (i < expression.Length && char.IsDigit(expression[i]))
                    {
                        operand = operand * 10 + (expression[i++] - '0');
                    }
                    i--;
                    operandStack.Push(operand);
                }
                else if (OperatorsDic.ContainsKey(c))
                {
                    while (operatorStack.Count > 0 &&
                        OperatorsDic[c] != null && operatorStack.Peek() != '(' &&
                        PrecedenceDic[operatorStack.Peek()] >= PrecedenceDic[c])
                    {
                        int b = operandStack.Pop();
                        int a = operandStack.Pop();
                        operandStack.Push(OperatorsDic[operatorStack.Pop()](a, b));
                    }
                    operatorStack.Push(c);
                }
                else if (c == '(')
                {
                    operatorStack.Push(c);
                }
                else if (c == ')')
                {
                    while (operatorStack.Peek() != '(')
                    {
                        int b = operandStack.Pop();
                        int a = operandStack.Pop();
                        operandStack.Push(OperatorsDic[operatorStack.Pop()](a, b));
                    }
                    operatorStack.Pop();
                }
            }

            while (operatorStack.Count > 0)
            {
                int b = operandStack.Pop();
                int a = operandStack.Pop();
                operandStack.Push(OperatorsDic[operatorStack.Pop()](a, b));
            }
            result = operandStack.Pop();

            return true;
        }
        catch (Exception)
        {
            return false;
        }
    }
}

那接下来测试一下代码,因为代码内只做了整形的计算,所以表达式也只用整形。

官方API

实际上微软官方在 System.Data 库中 DataTable.Compute(String, String)方法实现了计算表达式,代码如下

using System;
using System.Data;
using System.Text.RegularExpressions;

public class ArithmeticExpressionEvaluator
{
    public static bool IsArithmeticExpression(int arg, string str, out double result)
    {
        result = 0;

        // 验证字符串是否包含有效的四则运算表达式
        if (!IsValidArithmeticExpression(str) || !str.ToLower().Contains("x".ToLower()))
        {
            return false;
        }

        // 将字符串中的变量x替换为传入的整数arg
        string expression = str.Replace("x", arg.ToString());

        // 计算并返回表达式的值
        try
        {
            return double.TryParse(new DataTable().Compute(expression, "").ToString(), out result);
        }
        catch
        {
            return false;
        }
    }

    private static bool IsValidArithmeticExpression(string str)
    {
        // 使用正则表达式验证四则运算表达式的有效性
        string pattern = @"^[-+*/() x\d\s]+$";
        return Regex.IsMatch(str, pattern);
    }
}
class Program
{
    public static void Main()
    {
        while (true)
        {
            string expression = Console.ReadLine();
            string arg = Console.ReadLine();

            if (ArithmeticExpressionEvaluator.IsArithmeticExpression(int.Parse(arg), expression, out double result))
            {
                Console.WriteLine($"The result of the arithmetic expression is: {result}");
            }
            else
            {
                Console.WriteLine("The input string is not a valid arithmetic expression.");
            }
        }
    }
}

测试结果:

总结

刚开始拿到这个需求还是有点头疼的,想了很久的方案,突然想到之前看数据结构的书的时候,提到过栈在表达式求值中的应用,翻书看了一下,还是被这个实现方案惊艳到了,所以,还是需要多读多看多思考,才能在面对各种需求游刃有余,加油~文章来源地址https://www.toymoban.com/news/detail-548556.html

到了这里,关于编码技巧 --- 如何实现字符串运算表达式的计算的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【算法】用c#实现自定义字符串编码及围栏解码方法

    编写一个函数/方法,它接受2个参数、一个字符串和轨道数,并返回ENCODED字符串。 编写第二个函数/方法,它接受2个参数、一个编码字符串和轨道数,并返回DECODED字符串。 然后使用围栏密码对其进行解码。 这种密码用于通过将每个字符沿着一组“竖状轨道”依次放在对角线

    2024年02月12日
    浏览(38)
  • 12.字符串和正则表达式

    正则表达式相关知识 在编写处理字符串的程序或网页时,经常会有查找符合某些复杂规则的字符串的需要,正则表达式就是用于描述这些规则的工具,换句话说正则表达式是一种工具,它定义了字符串的匹配模式(如何检查一个字符串是否有跟某种模式匹配的部分或者从一个

    2024年01月16日
    浏览(62)
  • python 正则表达式提取字符串

    1、提取字符串的场景及公式、命令 背景 :目前遇到的场景主要是以某个字符串开始、某个字符串结束,提取中间部分的字符,有的时候需要开始的字符,有时不需要,大概涉及到了4种情况,场景及处理方式如下: 1.1 以某个字符开始、某个字符结束,期待的提取结果 包含

    2024年02月02日
    浏览(53)
  • notepad++ 正则表达式查找特定字符串

    批量文本的处理方法 在报文中有很多指标和值都具有固定的格式,比如是  a=\\\"1\\\" 这类格式,那么我们只取前面的指标a,就会比较复杂,而使用正则表达式就会快乐许多! 采用以下第二种方法 查找目标 =(.+?)\\\"    表示查找以等号开头,引号和空格  结尾的字符串,可以避免查

    2024年02月15日
    浏览(57)
  • java之字符串与正则表达式

    目录 String 构造方法 注意 格式控制字符串 常用方法 StringBuilder与StringBuffer 特点 理解可变与不可变 字符串拼接方法 字符串删除方法 字符串内插入字符 字符串替换方法 字符串反转方法 查字符串对应索引处的字符  截取字符串 正则表达式 正则表达式符号表 正则表达式常用方

    2023年04月22日
    浏览(45)
  • 【python】12.字符串和正则表达式

    正则表达式相关知识 在编写处理字符串的程序或网页时,经常会有查找符合某些复杂规则的字符串的需要,正则表达式就是用于描述这些规则的工具,换句话说正则表达式是一种工具,它定义了字符串的匹配模式(如何检查一个字符串是否有跟某种模式匹配的部分或者从一个

    2024年01月16日
    浏览(49)
  • Python中的字符串与字符编码

    Hello,这里是Token_w的博客,欢迎您的到来 今天文章讲解的是Python中的字符串与字符编码,其中有基础的理论知识讲解,也有实战中的应用讲解,希望对你有所帮助 整理不易,如对你有所帮助,希望能得到你的点赞、收藏支持。感谢 Python中的字符编码是个老生常谈的话题,同

    2024年02月12日
    浏览(62)
  • 正则表达式中 “$” 并不是表示 “字符串结束

    作者:Seth Larson 译者:豌豆花下猫@Python猫 英文:Regex character “$” doesn\\\'t mean “end-of-string” 转载请保留作者及译者信息! 这篇文章写一写我最近在用 Python 的正则表达式模块( re )开发 CPython 的 SBOM 工具时发现的一个令人惊讶的行为。 如果用过正则表达式,你可能知道 ^

    2024年04月15日
    浏览(39)
  • Python 自学(五) 之字符串及正则表达式

    目录 1. 字符串的分割合并  split()  join()         P132 2. 字符串的检索   count() find() index() startswith() endswith()         P134 3. 去除空格和特殊字符   strip()  lstrip() rstrip()          P139 4. 格式化字符串   format()         P142 5. 字符串编码转换  encode()  decode()        P145

    2024年01月25日
    浏览(51)
  • 【动态规划】【字符串】C++算法:正则表达式匹配

    视频算法专题 动态规划汇总 字符串 给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 ‘.’ 和 ‘ ’ 的正则表达式匹配。 ‘.’ 匹配任意单个字符 \\\' ’ 匹配零个或多个前面的那一个元素 所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。 示例 1: 输入:

    2024年02月03日
    浏览(57)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包