strconv.ParseFloat 不会 panic,真正 panic 的是后续对非法结果(如 NaN、除零)的运算;必须检查 err,正确处理空输入、全角符号和科学计数法,并优先使用 expr 等安全库解析表达式。

为什么 strconv.ParseFloat 会 panic 而不是返回 error?
因为传了空字符串或非法格式(比如 "123abc")时,strconv.ParseFloat 不会 panic,但如果你没检查返回的 err 就直接用结果,后续运算就可能出错——真正 panic 的往往是除零、溢出或对 NaN 做比较。常见现象是输入 "+" 或只敲回车后程序崩溃。
- 必须始终检查
err != nil,哪怕只是打印提示并跳过计算 -
strconv.ParseFloat(s, 64)的第二个参数固定写64,别写32后又拿结果参与 float64 运算,隐式转换不报错但精度丢失 - 如果想支持科学计数法(如
"1e2"),ParseFloat本身支持,但用户输"1e"就会失败——得提前用正则粗筛或捕获错误后给更友好的提示
如何安全地解析带运算符的字符串,比如 "3 + 4 * 2"?
别手写状态机或递归下降——初级项目里用现成的轻量库更稳。gval 或 expr 是两个可选方案,但 expr 更专注数学表达式,语法干净,且默认禁用任意代码执行(不像 gval 默认允许函数调用)。
- 引入
github.com/antonmedv/expr后,一句expr.Eval("3 + 4 * 2", nil)就能得11.0 - 它默认只接受数字、四则运算、括号和负号;输入
"os.Exit(1)"会直接报unknown name "os" - 注意:它返回的是
interface{},需断言为float64,若表达式结果是整数(如"4"),仍会是float64(4)类型,不用额外处理 - 性能上,每次调用都做词法+语法分析,频繁计算建议缓存
expr.Compile后的Program,但命令行计算器每轮只算一次,没必要
为什么用 bufio.Scanner 读输入,而不是 fmt.Scanln?
因为 fmt.Scanln 遇到空格就截断,输 "12 + 3" 会只读到 "12";而 bufio.Scanner 默认按行读,完整拿到用户键入的整个字符串,适合表达式解析。
- 别忘了设置缓冲区大小:
scanner.Buffer(make([]byte, 512), 1024),否则超长表达式(比如嵌套几十层括号)会触发"token too long"错误 -
scanner.Scan()返回false时,要通过scanner.Err()判断是 EOF 还是真实错误;Ctrl+D 或 Ctrl+Z 触发 EOF 是正常退出,不用报错 - 如果用户输完按回车,
scanner.Text()结尾不含换行符,无需strings.TrimSpace——但用户可能多打空格,留着也无害,expr会自动忽略
怎么让加减乘除支持负数开头,比如 "-5 * 2"?
expr 库原生支持,但前提是你的输入字符串里负号是 ASCII 减号('-'),不是中文全角减号或连接号。真实场景中,用户从网页复制、微信粘贴进来,极易混入 −(U+2212)这类符号,导致解析失败。
立即学习“go语言免费学习笔记(深入)”;
- 在调用
expr.Eval前,先做一次简单替换:strings.ReplaceAll(input, "−", "-")(注意第一个是全角) - 更彻底的做法是用
unicode.IsSymbol扫描,把所有看起来像运算符的 Unicode 符号统一转成 ASCII 版本,但命令行输入几乎不会出现,优先解决粘贴场景即可 - 另外,
expr不支持前置++或--,输"++3"会报错;这不是 bug,是设计如此——它只认标准数学表达式语法
最易被忽略的一点:用户输入的空行或纯空白字符,scanner.Text() 返回空字符串,直接喂给 expr.Eval 会报 syntax error: unexpected EOF。这个错误信息对用户不友好,得单独判断空输入并跳过。










