命令行计算器:接受用户输入的算数表达式,并输出结果
本文最后更新于187 天前,其中的信息可能已经过时,如有错误请发送邮件到big_fw@foxmail.com

go 命令行计算器:接受用户输入的算数表达式,并输出结果

示例

请输入一个计算式:
-9*8*(-2)-88/5
-9*8*(-2)-88/5的值为:
126.4
进程 已完成,退出代码为 0

思路:

使用栈先入后出的特点和计算符的优先级,把获取的字符串转为后缀表达式,并同时使用栈来计算结果

1.定义符号优先级(使用map映射绑定)

2.定义栈

3.中缀表达式转后缀表达式函数

4.计算后缀表达式

5.后缀表达式计算函数

6.输入式子合法性检测

7.去除结果小数点后面多余的零

各部分代码

1.定义符号优先级(使用map映射绑定)

var priority = map[rune]int{
    '%': 2,
    '+': 1,
    '-': 1,
    '*': 2,
    '/': 2,
    '(': 0,
}

2.定义栈结构

// 实现栈结构
type SliceStack struct {
    dataStack []interface{}
}

// 创建一个新的初始栈
func NewSliceStack() *SliceStack {
    return &SliceStack{}
}

// 在栈顶放入数据
func (s *SliceStack) Push(data interface{}) {
    s.dataStack = append(s.dataStack, data)
    //append函数:在原切片的末尾添加元素
}

// 栈最上面的数据出栈
func (s *SliceStack) Pop() interface{} {
    if len(s.dataStack) == 0 {
        return nil
    }
    slice := s.dataStack[len(s.dataStack)-1]
    s.dataStack = s.dataStack[:len(s.dataStack)-1]
    //把栈顶下移一位
    return slice
}

// 查看栈顶第一个元素
func (s *SliceStack) Peek() interface{} {
    if len(s.dataStack) == 0 {
        return nil
    }
    return s.dataStack[len(s.dataStack)-1]
}

// 判断栈是否空
func (s *SliceStack) IsEmpty() bool {
    return len(s.dataStack) == 0
}

3.中缀表达式转后缀表达式函数

// 中缀表达式转后缀表达式
func convert(shuru string) []interface{} {
    //定义一个存放处理后的字符的切片
    var inter []interface{}
    //创建一个栈,用于存放运算符
    houzhui := NewSliceStack()
    for i := 0; i < len(shuru); i++ {
        //转换输入的字段为rune类型方便值的判断
        char := rune(shuru[i])
        //字符为空格时,跳过此次循环
        if char == ' ' {
            continue
        }
        //处理正负数字
        if unicode.IsDigit(char) || char == '.' || (char == '-' && (i == 0 || (i > 0 && (rune(shuru[i-1]) == '(' || rune(shuru[i-1]) == '+' || rune(shuru[i-1]) == '-' || rune(shuru[i-1]) == '*' || rune(shuru[i-1]) == '/')))) {
            num := ""
            if char == '-' {
                num += string(char)
                i++
                char = rune(shuru[i])
            }
            for i < len(shuru) && (unicode.IsDigit(char) || char == '.') {
                num += string(char)
                i++
                if i < len(shuru) {
                    char = rune(shuru[i])
                }
            }
            i-- //回退一部,因为外层也有一个 i++
            //将字符转化为浮点型
            value, _ := strconv.ParseFloat(num, 64)
            //将处理过后的数据放入inter切片中存放
            inter = append(inter, value)
            //在符号为"("的情况下直接放入字符栈中
        } else if char == '(' {
            houzhui.Push(char)
        } else if char == ')' {
            //在存放运算符的栈不为空,并且栈顶字符不为"("的情况下
            for !houzhui.IsEmpty() && houzhui.Peek().(rune) != '(' {
                //把运算符栈中的运算符取出放进inter中
                inter = append(inter, houzhui.Pop())
            }
            //取出栈中"("
            houzhui.Pop()
        } else {
            //处理运算符优先级
            //在栈不为空,并且新的运算符优先级比栈顶的小的情况下
            for !houzhui.IsEmpty() && priority[houzhui.Peek().(rune)] >= priority[char] {
                //把运算符放入inter中
                inter = append(inter, houzhui.Pop())
            }
            houzhui.Push(char)
        }
    }
    //把剩余的操作符全部放入inter切片中
    for !houzhui.IsEmpty() {
        inter = append(inter, houzhui.Pop())
    }
    //返回切片
    return inter
}

计算后缀表达式

// 计算后缀表达式
// 计算后缀表达式
func computeDpostfixshuru(inter []interface{}) float64 {
    //创建一个用存放数值
    stored := NewSliceStack()
    //遍历数值
    for _, value := range inter {
        //判断遍历数值的类型
        switch value.(type) {
        case float64:
            //当类型为float64时,把值压入栈中
            stored.Push(value)
        case rune:
            //当值为rune时判定为计算符,调用计算函数
            calculatingFunction(stored, value.(rune))
        }
    }
    //返回栈低最后一个值作为结果
    return stored.Pop().(float64)
}

计算函数

// 计算函数
func calculatingFunction(stored *SliceStack, operator rune) {
    //取出两个操作数
    num1 := stored.Pop().(float64)
    num2 := stored.Pop().(float64)
    var result float64
    switch operator {
    case '+':
        result = num2 + num1
    case '-':
        result = num2 - num1
    case '*':
        result = num2 * num1
    case '/':
    //被除数为零时输出错误和终止程序
        if num1 == 0 {
            fmt.Println("错误:计算过程中出现被除数等于零的情况,请检查算式合法性")
            os.Exit(1)
        } //除法注意先后顺续不要错
        result = num2 / num1
    case '%':
        result = math.Mod(num2, num1)
    }
    //将结果压回栈
    stored.Push(result)
}

6.检测输入式子合法性

func validateshuru(expr string) error {
    // 移除空格
    expr = strings.ReplaceAll(expr, " ", "")
    // 检查是否包含非法字符
    if matched, _ := regexp.MatchString(`[^0-9+\-*/().% ]`, expr); matched {
        return fmt.Errorf("错误:输入字符不合法")
    }
    // 检查括号是否成对出现
    if strings.Count(expr, "(") != strings.Count(expr, ")") {
        return fmt.Errorf("错误:输入括号不匹配")
    }
    // 检查开头和结尾是否为非法字符
    if matched, _ := regexp.MatchString(`^[*/%]|\s$`, expr); matched {
        return fmt.Errorf("错误:开头或者结尾字符不合法")
    }
    // 检查连续的操作符
    if matched, _ := regexp.MatchString(`\+\+|--|\*\*|%%|\(\)|//`, expr); matched {
        return fmt.Errorf("错误:出现连续的符号")
    }
    if matched, _ := regexp.MatchString(`/0`, expr); matched {
        return fmt.Errorf("错误:0不能出现在/0后面")
    }
    return nil
}

7.动态去除小数点后面的零

// 动态去除小数点后的零
func removeZero(f float64) string {
    s := fmt.Sprintf("%f", f)
    s = strings.TrimRight(s, "0")
    //检查移除多余零最后一位是否为‘.’结尾
    if strings.HasSuffix(s, ".") {
        s = strings.TrimRight(s, ".")
    }
    return s
}

完整代码

package main

import (
    "bufio"
    "fmt"
    "math"
    "os"
    "regexp"
    "strconv"
    "strings"
    "unicode"
)

// 1.定义符号优先级
// 2.定义栈
// 3.中缀转后缀表达式
// 4.计算后缀表达式
// 5.计算函数
// 6.检验输入式子合法性
// 7.动态去除小数点后的零

// map映射绑定字符优先级
var priority = map[rune]int{
    '%': 2,
    '+': 1,
    '-': 1,
    '*': 2,
    '/': 2,
    '(': 0,
}

// 实现栈结构
type SliceStack struct {
    dataStack []interface{}
}

// 创建一个新的初始栈
func NewSliceStack() *SliceStack {
    return &SliceStack{}
}

// 在栈顶放入数据
func (s *SliceStack) Push(data interface{}) {
    s.dataStack = append(s.dataStack, data)
    //append函数:在原切片的末尾添加元素
}

// 栈最上面的数据出栈
func (s *SliceStack) Pop() interface{} {
    if len(s.dataStack) == 0 {
        return nil
    }
    slice := s.dataStack[len(s.dataStack)-1]
    s.dataStack = s.dataStack[:len(s.dataStack)-1]
    //把栈顶下移一位
    return slice
}

// 查看栈顶第一个元素
func (s *SliceStack) Peek() interface{} {
    if len(s.dataStack) == 0 {
        return nil
    }
    return s.dataStack[len(s.dataStack)-1]
}

// 判断栈是否空
func (s *SliceStack) IsEmpty() bool {
    return len(s.dataStack) == 0
}

// 中缀表达式转后缀表达式
func convert(shuru string) []interface{} {
    //定义一个存放处理后的字符的切片
    var inter []interface{}
    //创建一个栈,用于存放运算符
    houzhui := NewSliceStack()
    for i := 0; i < len(shuru); i++ {
        //转换输入的字段为rune类型方便值的判断
        char := rune(shuru[i])
        //字符为空格时,跳过此次循环
        if char == ' ' {
            continue
        }
        //处理正负数字
        if unicode.IsDigit(char) || char == '.' || (char == '-' && (i == 0 || (i > 0 && (rune(shuru[i-1]) == '(' || rune(shuru[i-1]) == '+' || rune(shuru[i-1]) == '-' || rune(shuru[i-1]) == '*' || rune(shuru[i-1]) == '/')))) {
            num := ""
            if char == '-' {
                num += string(char)
                i++
                char = rune(shuru[i])
            }
            for i < len(shuru) && (unicode.IsDigit(char) || char == '.') {
                num += string(char)
                i++
                if i < len(shuru) {
                    char = rune(shuru[i])
                }
            }
            i-- //回退一部,因为外层也有一个 i++
            //将字符转化为浮点型
            value, _ := strconv.ParseFloat(num, 64)
            //将处理过后的数据放入inter切片中存放
            inter = append(inter, value)
            //在符号为"("的情况下直接放入字符栈中
        } else if char == '(' {
            houzhui.Push(char)
        } else if char == ')' {
            //在存放运算符的栈不为空,并且栈顶字符不为"("的情况下
            for !houzhui.IsEmpty() && houzhui.Peek().(rune) != '(' {
                //把运算符栈中的运算符取出放进inter中
                inter = append(inter, houzhui.Pop())
            }
            //取出栈中"("
            houzhui.Pop()
        } else {
            //处理运算符优先级
            //在栈不为空,并且新的运算符优先级比栈顶的小的情况下
            for !houzhui.IsEmpty() && priority[houzhui.Peek().(rune)] >= priority[char] {
                //把运算符放入inter中
                inter = append(inter, houzhui.Pop())
            }
            houzhui.Push(char)
        }
    }
    //把剩余的操作符全部放入inter切片中
    for !houzhui.IsEmpty() {
        inter = append(inter, houzhui.Pop())
    }
    //返回切片
    return inter
}

// 计算后缀表达式
func computeDpostfixshuru(inter []interface{}) float64 {
    //创建一个用存放数值
    stored := NewSliceStack()
    //遍历数值
    for _, value := range inter {
        //判断遍历数值的类型
        switch value.(type) {
        case float64:
            //当类型为float64时,把值压入栈中
            stored.Push(value)
        case rune:
            //当值为rune时判定为计算符,调用计算函数
            calculatingFunction(stored, value.(rune))
        }
    }
    //返回栈低最后一个值作为结果
    return stored.Pop().(float64)
}

// 计算函数
func calculatingFunction(stored *SliceStack, operator rune) {
    //取出两个操作数
    num1 := stored.Pop().(float64)
    num2 := stored.Pop().(float64)
    var result float64
    switch operator {
    case '+':
        result = num2 + num1
    case '-':
        result = num2 - num1
    case '*':
        result = num2 * num1
    case '/':
        if num1 == 0 {
            fmt.Println("错误:计算过程中出现被除数等于零的情况,请检查算式合法性")
            os.Exit(1)
        } //除法注意先后顺续不要错
        result = num2 / num1
    case '%':
        result = math.Mod(num2, num1)
    }
    //将结果压回栈
    stored.Push(result)
}

// 式子合法性判断
func validateshuru(expr string) error {
    // 移除空格
    expr = strings.ReplaceAll(expr, " ", "")
    // 检查是否包含非法字符
    if matched, _ := regexp.MatchString(`[^0-9+\-*/().% ]`, expr); matched {
        return fmt.Errorf("错误:输入字符不合法")
    }
    // 检查括号是否成对出现
    if strings.Count(expr, "(") != strings.Count(expr, ")") {
        return fmt.Errorf("错误:输入括号不匹配")
    }
    // 检查开头和结尾是否为非法字符
    if matched, _ := regexp.MatchString(`^[*/%]|\s$`, expr); matched {
        return fmt.Errorf("错误:开头或者结尾字符不合法")
    }
    // 检查连续的操作符
    if matched, _ := regexp.MatchString(`\+\+|--|\*\*|%%|\(\)|//`, expr); matched {
        return fmt.Errorf("错误:出现连续的符号")
    }

    if matched, _ := regexp.MatchString(`/0`, expr); matched {
        return fmt.Errorf("错误:0不能出现在/0后面")
    }
    return nil
}

// 动态去除小数点后的零
func removeZero(f float64) string {
    s := fmt.Sprintf("%f", f)
    s = strings.TrimRight(s, "0")
    //检查移除多余零最后一位是否为‘.’结尾
    if strings.HasSuffix(s, ".") {
        s = strings.TrimRight(s, ".")
    }
    return s
}

func main() {
    fmt.Println("请输入一个计算式:")
    // 创建一个新的读取器
    reader := bufio.NewReader(os.Stdin)

    // 读取输入的一整行
    shuru, err := reader.ReadString('\n')
    if err != nil {
        fmt.Println("读取输入时出错:", err)
        return
    }
    shuru = strings.TrimSuffix(shuru, "\n")
    panduan := validateshuru(shuru)
    if panduan == nil {
        postfix := convert(shuru)
        result := computeDpostfixshuru(postfix)
        results := removeZero(result)
        fmt.Printf(" %s 的值为:\n%v", shuru, results)
    } else {
        fmt.Println(panduan)
    }
}
文末附加内容
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇