命令行计算器
程序员文章站
2022-05-24 23:53:46
...
在命令行下操作时,有时需要做一些计算。这时再打开图形界面的计算器比较麻烦,而Linux下的bc只能做一些简单的计算。使用C++实现命令行下计算器。
一、程序功能
- 执行+,-,*,/,%(取两个整数相除的余数),^(乘方)运算
- 程序可以执行的函数有
sin
,cos
,tan
,cot
,sec
,csc
,exp
,asin
,acos
,atan
,ln
,log
,log2
,log10
,sqrt(开二次方)
,abs(取绝对值)
,cbrt(开三次方)
,floor
,fix
,ceil
,round
,sum(求和)
,aver(求平均)
,min(取最小值)
,max(取最大值)
,rem(取两个整数相除的商)
- 程序默认使用弧度制,要使用角度制,执行
angle=1
;取消角度制,执行angle=0
- 定义的常数有: pi, e
- 使用方式
直接命令行下执行calc expression,或者输入calc进入程序后计算 - 退出程序执行
exit
或q
二、运行效果
三、程序代码
- main.cpp
#include "calculator.h"
#include <iostream>
#include <string>
bool set(slj::Calculator& calc, const std::string& str)
{
if (str == "angle=0") {
calc.set_angle(true);
return true;
}
if (str == "angle=1") {
calc.set_angle(false);
return true;
}
return false;
}
int main(int argc, char* argv[])
{
if (argc > 2) {
std::cout << "Usage: calc or calc expression" << std::endl;
return 0;
}
std::string eps;
real result;
slj::Calculator calc;
if (argc == 2) {
eps = argv[1];
calc.set(eps);
result = calc.calculate();
if (calc.error(eps)) {
std::cout << " ans \t = " << std::endl;
std::cout << "\t\t" << result << std::endl;
return 0;
} else {
std::cout << eps << std::endl;
return 0;
}
return 0;
}
std::cout << "Command line calculator." << std::endl;
std::cout << "Writen by Liangjin Song." << std::endl
<< std::endl;
do {
std::cout << "> ";
std::cin >> eps;
if (eps == "exit" || eps == "q") {
break;
}
if (set(calc, eps)) {
continue;
}
calc.set(eps);
result = calc.calculate();
if (calc.error(eps)) {
std::cout << " ans \t = " << std::endl;
std::cout << "\t\t" << result << std::endl;
} else {
std::cout << eps << std::endl;
}
} while (true);
return 0;
}
- calculator.h
/* the Calc class
* writen by Liangjin Song on 20200209
*/
#ifndef CALCULATOR_H
#define CALCULATOR_H
#include "strcase.h"
#include <string>
#include <vector>
#define stor std::stold
using real = long double;
namespace slj {
class Calculator {
private:
enum class Symbol {
digit,
add, // +
sub, // -
mult, // *
div, // /
power, // ^
mod, // %
lbrac, // (
rbrac, // )
point, // .
sin,
cos,
tan,
cot,
sec,
csc,
exp,
asin,
acos,
atan,
e,
pi,
ln,
sqrt,
abs,
cbrt,
letter,
err,
floor,
fix,
ceil,
round,
log10,
log2,
sign,
comma,
sum,
aver,
log,
min,
max,
rem
};
enum class Error {
unable_to_identify,
divide_zero,
expression_error
};
private:
std::string eps, eps_bak; // the expression
std::string err; // the error information
private:
bool cont; // is continue
bool radian; // radian or angle, default is ragian(true)
real to_angle(const real radn) const { return 180 * radn / pi; }
real to_radian(const real ang) const { return ang * pi / 180; }
int call; // the deep of the recursion function
private:
const real pi = 3.1415926535897932384626433832795028841971693993751;
const real e = 2.718281828459045235360287471352662497757247093699959574966967627724;
private:
void set_err(Error err, std::string str = ""); // set the error information
void set_err(Error err, char ch);
private:
Symbol resolve(std::string& dig); // resolve the expression
Symbol resolve(const char ch);
void digit(std::string& dig);
Symbol letter(std::string& str);
void remove(std::string& str);
private:
bool is_digits(std::string& str);
bool is_integer(std::string& str);
bool is_real(std::string& str);
private:
void apply(Symbol sym, std::vector<real>& val, std::vector<Symbol>& sign, std::string& str, real& rs);
void apply(std::vector<real>& val, std::vector<Symbol>& sign, real v);
void apply_last(std::vector<real>& val, std::vector<Symbol>& sign, real& rs);
real calcall(std::vector<real>& val, std::vector<Symbol>& sign);
public:
Calculator(const std::string& e = "");
~Calculator() = default;
public:
void set(const std::string& e);
real calculate();
bool error(std::string& err);
void set_angle(bool ang = true);
private:
bool sum, aver;
bool log;
bool min, max;
bool rem;
};
}
#endif // CALCULATOR_H
- calculator.cpp
#include "calculator.h"
#include <algorithm>
#include <cmath>
namespace slj {
Calculator::Calculator(const std::string& e)
{
set(e);
radian = true;
}
void Calculator::set(const std::string& e)
{
eps_bak = eps = e;
call = 0;
cont = true;
err = "";
sum = false;
aver = false;
this->log = false;
min = max = false;
rem = false;
}
void Calculator::set_err(Error err, std::string str)
{
switch (err) {
case Error::unable_to_identify:
this->err = "Unable to identify: ";
this->err += str;
cont = false;
break;
case Error::divide_zero:
this->err = "The divisor cannot not be 0! ";
cont = false;
break;
case Error::expression_error:
this->err = "Expression error!";
cont = false;
break;
default:
this->err = "";
break;
}
}
void Calculator::set_err(Error err, char ch)
{
char s1[2] = { ch, 0 };
set_err(err, s1);
}
real Calculator::calculate()
{
Symbol sym;
std::string sig;
real result = 0;
std::vector<real> val;
std::vector<Symbol> sign;
do {
sym = resolve(sig);
if (sym == Symbol::err) {
break;
}
apply(sym, val, sign, sig, result);
if (sym == Symbol::rbrac) {
return result;
}
} while (cont && eps.size());
if (!cont) {
return 0;
}
return calcall(val, sign);
}
Calculator::Symbol Calculator::resolve(std::string& sig)
{
char ch = eps[0];
Symbol sym = resolve(ch);
switch (sym) {
case Symbol::digit:
digit(sig);
if (!is_real(sig)) {
cont = false;
sym = Symbol::err;
set_err(Error::unable_to_identify, sig);
return sym;
}
break;
case Symbol::letter:
sym = letter(sig);
break;
case Symbol::add:
sig = "+";
break;
case Symbol::sub:
sig = "-";
break;
case Symbol::mult:
sig = "*";
break;
case Symbol::div:
sig = "/";
break;
case Symbol::mod:
sig = "%";
break;
case Symbol::power:
sig = "^";
break;
case Symbol::lbrac:
sig = "(";
break;
case Symbol::rbrac:
sig = ")";
break;
case Symbol::comma:
sig = ",";
break;
default:
break;
}
if (sym != Symbol::err) {
remove(sig);
}
return sym;
}
Calculator::Symbol Calculator::resolve(const char ch)
{
Symbol sym = Symbol::err;
switch (ch) {
case '(':
sym = Symbol::lbrac;
break;
case ')':
sym = Symbol::rbrac;
break;
case '.':
sym = Symbol::point;
break;
case '+':
sym = Symbol::add;
break;
case '-':
sym = Symbol::sub;
break;
case '*':
sym = Symbol::mult;
break;
case '/':
sym = Symbol::div;
break;
case '^':
sym = Symbol::power;
break;
case '%':
sym = Symbol::mod;
break;
case ',':
sym = Symbol::comma;
break;
default:
break;
}
if (ch >= 0x30 && ch <= 0x39) {
sym = Symbol::digit;
}
if (ch >= 0x61 && ch <= 0x7a) {
sym = Symbol::letter;
}
if (sym == Symbol::err) {
cont = false;
set_err(Error::unable_to_identify, ch);
}
return sym;
}
void Calculator::digit(std::string& dig)
{
char ch = 0, s[2] = { 0, 0 };
const size_t ns = eps.size();
dig = "";
for (size_t i = 0; i < ns; ++i) {
ch = eps[i];
if (!(resolve(ch) == Symbol::digit || resolve(ch) == Symbol::point)) {
break;
}
s[0] = ch;
dig += s;
}
}
bool Calculator::is_digits(std::string& str)
{
return std::all_of(str.begin(), str.end(), ::isdigit);
}
bool Calculator::is_integer(std::string& str)
{
if (str.size() <= 0) {
return false;
}
if (is_digits(str)) {
return true;
}
if (str.size() > 1) {
if (str[0] == '-' || str[0] == '+') {
std::string tstr = str.substr(1, str.size() - 1);
return is_digits(tstr);
}
return false;
}
return false;
}
bool Calculator::is_real(std::string& str)
{
if (is_integer(str)) {
return true;
}
size_t n1 = str.find_first_of('.');
size_t n2 = str.find_last_of('.');
if (n1 != n2 || n1 == str.size() - 1) {
return false;
}
std::string inte = str.substr(0, n1);
std::string frac = str.substr(n1 + 1, str.size() - n1);
return is_integer(inte) && is_digits(frac);
}
Calculator::Symbol Calculator::letter(std::string& str)
{
char ch = eps[0], s[2] = { ch, 0 };
const size_t ns = eps.size();
str = s;
if (ch == 'e') {
if (ns == 1) {
return Symbol::e;
}
ch = eps[1];
if (ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '^' || ch == ')' || ch == '%') {
return Symbol::e;
} else {
cont = false;
set_err(Error::unable_to_identify, ch);
return Symbol::err;
}
}
if (ch == 'p') {
if (ns == 1) {
cont = false;
set_err(Error::unable_to_identify, ch);
return Symbol::err;
}
ch = eps[1];
if (ch == 'i') {
if (ns == 2) {
str = "pi";
return Symbol::pi;
}
ch = eps[2];
if (ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '^' || ch == ')' || ch == '%') {
str = "pi";
return Symbol::pi;
} else {
cont = false;
str = "pi";
s[0] = ch;
str += s;
set_err(Error::unable_to_identify, str);
return Symbol::err;
}
} else {
cont = false;
s[0] = ch;
str += s;
set_err(Error::unable_to_identify, str);
return Symbol::err;
}
}
if (ns < 5) {
cont = false;
set_err(Error::unable_to_identify, eps);
return Symbol::err;
}
str = eps.substr(0, 3);
if (str == "ln(") {
str = "ln";
return Symbol::ln;
}
Symbol sym = Symbol::err;
str = eps.substr(0, 4);
switch (hash_(str.c_str())) {
case "sin("_hash:
str = "sin";
sym = Symbol::sin;
break;
case "cos("_hash:
str = "cos";
sym = Symbol::cos;
break;
case "tan("_hash:
str = "tan";
sym = Symbol::tan;
break;
case "cot("_hash:
str = "cot";
sym = Symbol::cot;
break;
case "sec("_hash:
str = "sec";
sym = Symbol::sec;
break;
case "csc("_hash:
str = "csc";
sym = Symbol::csc;
break;
case "log("_hash:
str = "log";
sym = Symbol::log;
break;
case "abs("_hash:
str = "abs";
sym = Symbol::abs;
break;
case "exp("_hash:
str = "exp";
sym = Symbol::exp;
break;
case "fix("_hash:
str = "fix";
sym = Symbol::fix;
break;
case "sum("_hash:
str = "sum";
sym = Symbol::sum;
break;
case "min("_hash:
str = "min";
sym = Symbol::min;
break;
case "max("_hash:
str = "max";
sym = Symbol::max;
break;
case "rem("_hash:
str = "rem";
sym = Symbol::rem;
break;
default:
break;
}
if (sym != Symbol::err) {
return sym;
}
if (ns < 7) {
cont = false;
set_err(Error::unable_to_identify, eps);
return Symbol::err;
}
str = eps.substr(0, 5);
switch (hash_(str.c_str())) {
case "sqrt("_hash:
str = "sqrt";
sym = Symbol::sqrt;
break;
case "cbrt("_hash:
str = "cbrt";
sym = Symbol::cbrt;
break;
case "asin("_hash:
str = "asin";
sym = Symbol::asin;
break;
case "acos("_hash:
str = "acos";
sym = Symbol::acos;
break;
case "atan("_hash:
str = "atan";
sym = Symbol::atan;
break;
case "ceil("_hash:
str = "ceil";
sym = Symbol::ceil;
break;
case "aver("_hash:
str = "aver";
sym = Symbol::aver;
break;
case "log2("_hash:
str = "log2";
sym = Symbol::log2;
break;
case "sign("_hash:
str = "sign";
sym = Symbol::sign;
break;
default:
break;
}
if (sym != Symbol::err) {
return sym;
}
if (ns < 8) {
cont = false;
set_err(Error::unable_to_identify, eps);
return Symbol::err;
}
str = eps.substr(0, 6);
switch (hash_(str.c_str())) {
case "floor("_hash:
str = "floor";
sym = Symbol::floor;
break;
case "round("_hash:
str = "round";
sym = Symbol::round;
break;
case "log10("_hash:
str = "log10";
sym = Symbol::log10;
break;
default:
break;
}
return sym;
}
void Calculator::remove(std::string& str)
{
const size_t ns = str.size();
const size_t ne = eps.size();
if (ns == ne) {
eps = "";
return;
}
eps = eps.substr(ns, ne - ns);
}
void Calculator::apply(Symbol sym, std::vector<real>& val,
std::vector<Symbol>& sign, std::string& str, real& rs)
{
real v = 0;
switch (sym) {
case Symbol::digit:
v = stor(str);
apply(val, sign, v);
break;
case Symbol::e:
v = e;
apply(val, sign, v);
break;
case Symbol::pi:
v = pi;
apply(val, sign, v);
break;
case Symbol::lbrac:
++call;
rs = this->calculate();
apply_last(val, sign, rs);
break;
case Symbol::rbrac:
--call;
rs = calcall(val, sign);
break;
case Symbol::sum:
sum = true;
sign.push_back(sym);
break;
case Symbol::aver:
aver = true;
sign.push_back(sym);
break;
case Symbol::log:
this->log = true;
sign.push_back(sym);
break;
case Symbol::min:
min = true;
sign.push_back(sym);
break;
case Symbol::max:
max = true;
sign.push_back(sym);
break;
case Symbol::rem:
rem = true;
sign.push_back(sym);
break;
default:
sign.push_back(sym);
break;
}
}
void Calculator::apply(std::vector<real>& val, std::vector<Symbol>& sign, real v)
{
if (sign.size() == 0) {
val.push_back(v);
return;
}
if (val.size() == 0) {
if (sign.back() == Symbol::sub) {
sign.pop_back();
val.push_back(-v);
return;
} else {
cont = false;
}
}
real v2;
switch (sign.back()) {
case Symbol::mult:
v2 = val.back();
val.pop_back();
sign.pop_back();
val.push_back(v * v2);
break;
case Symbol::div:
if (v == 0) {
cont = false;
set_err(Error::divide_zero, "");
}
v2 = val.back();
val.pop_back();
sign.pop_back();
val.push_back(v2 / v);
break;
case Symbol::power:
v2 = val.back();
val.pop_back();
sign.pop_back();
val.push_back(powl(v2, v));
break;
case Symbol::mod:
if ((size_t)v == 0) {
cont = false;
set_err(Error::divide_zero, "");
}
v2 = val.back();
val.pop_back();
sign.pop_back();
val.push_back((size_t)v2 % (size_t)v);
break;
// case Symbol::sub:
// sign[sign.size() - 1] = Symbol::add;
// val.push_back(-v);
// break;
default:
val.push_back(v);
break;
}
}
real Calculator::calcall(std::vector<real>& val, std::vector<Symbol>& sign)
{
const size_t nval = val.size();
if (sign.size() == 0) {
if (nval == 1) {
return val.back();
} else {
set_err(Error::expression_error, "");
return 0;
}
}
real v1 = 0, v2 = 0;
if (this->log) {
if ((nval != 2) && (sign.size() != 1)) {
set_err(Error::expression_error);
return 0;
}
return log10l(val[1]) / log10l(val[0]);
}
if (this->rem) {
if ((nval != 2) && (sign.size() != 1)) {
set_err(Error::expression_error);
return 0;
}
if (val[1] == 0) {
set_err(Error::divide_zero);
return 0;
}
return (long long)val[0] / (long long)val[1];
}
if (sum || aver) {
if (sign.size() != nval - 1) {
set_err(Error::expression_error, "");
return 0;
}
v1 = 0;
for (auto& i : val) {
v1 += i;
}
if (sum) {
return v1;
}
if (aver) {
return v1 / nval;
}
}
if (min || max) {
if (sign.size() != nval - 1) {
set_err(Error::expression_error, "");
return 0;
}
v1 = v2 = val[0];
for (auto& i : val) {
if (v1 > i) {
v1 = i;
}
if (v2 < i) {
v2 = i;
}
}
if (min) {
return v1;
}
if (max) {
return v2;
}
}
if (sign.size() != nval - 1) {
set_err(Error::expression_error, "");
return 0;
}
for(size_t i=0; i<sign.size();++i){
v1=val[i];
v2=val[i+1];
switch (sign[i])
{
case Symbol::add:
val[i+1]=v1+v2;
break;
case Symbol::sub:
val[i+1]=v1-v2;
break;
default:
break;
}
}
/*
do {
switch (sign.front()) {
case Symbol::add:
if (nval < 2) {
set_err(Error::expression_error, "");
return 0;
}
v1 = val.front();
val.pop_back();
v2 = val.front();
val.pop_back();
val.push_back(v1 + v2);
break;
case Symbol::sub:
if (nval < 2) {
set_err(Error::expression_error, "");
return 0;
}
v1 = val.back();
val.pop_back();
v2 = val.back();
val.pop_back();
val.push_back(v2 - v1);
break;
default:
set_err(Error::expression_error, "");
break;
}
sign.pop_back();
} while (sign.size() != 0);
*/
return val.back();
}
void Calculator::apply_last(std::vector<real>& val, std::vector<Symbol>& sign, real& rs)
{
if (sign.size() == 0) {
return;
}
switch (sign.back()) {
case Symbol::power:
if (val.size() == 0) {
set_err(Error::expression_error, "");
return;
}
sign.pop_back();
val[val.size() - 1] = powl(val.back(), rs);
break;
case Symbol::sin:
if (!radian) {
rs = to_radian(rs);
}
sign.pop_back();
val.push_back(sinl(rs));
break;
case Symbol::cos:
if (!radian) {
rs = to_radian(rs);
}
sign.pop_back();
val.push_back(cosl(rs));
break;
case Symbol::tan:
if (!radian) {
rs = to_radian(rs);
}
sign.pop_back();
val.push_back(tanl(rs));
break;
case Symbol::cot:
if (!radian) {
rs = to_radian(rs);
}
sign.pop_back();
val.push_back((real)1 / tanl(rs));
break;
case Symbol::sec:
if (!radian) {
rs = to_radian(rs);
}
sign.pop_back();
val.push_back((real)1 / cosl(rs));
break;
case Symbol::csc:
if (!radian) {
rs = to_radian(rs);
}
sign.pop_back();
val.push_back((real)1 / sinl(rs));
break;
case Symbol::exp:
sign.pop_back();
val.push_back(expl(rs));
break;
case Symbol::asin:
sign.pop_back();
rs = asinl(rs);
if (!radian) {
rs = to_angle(rs);
}
val.push_back(rs);
break;
case Symbol::acos:
sign.pop_back();
rs = acosl(rs);
if (!radian) {
rs = to_angle(rs);
}
val.push_back(rs);
break;
case Symbol::atan:
sign.pop_back();
rs = atanl(rs);
if (!radian) {
rs = to_angle(rs);
}
val.push_back(rs);
break;
case Symbol::ln:
sign.pop_back();
val.push_back(logl(rs));
break;
case Symbol::log:
sign.pop_back();
val.push_back(rs);
this->log = false;
break;
case Symbol::sqrt:
sign.pop_back();
val.push_back(sqrtl(rs));
break;
case Symbol::abs:
sign.pop_back();
val.push_back(fabsl(rs));
break;
case Symbol::cbrt:
sign.pop_back();
val.push_back(cbrtl(rs));
break;
case Symbol::floor:
sign.pop_back();
val.push_back(floorl(rs));
break;
case Symbol::fix:
sign.pop_back();
val.push_back((long long)rs);
break;
case Symbol::ceil:
sign.pop_back();
val.push_back(ceill(rs));
break;
case Symbol::round:
sign.pop_back();
val.push_back(roundl(rs));
break;
case Symbol::sum:
sign.pop_back();
val.push_back(rs);
sum = false;
break;
case Symbol::aver:
sign.pop_back();
val.push_back(rs);
aver = false;
break;
case Symbol::log10:
sign.pop_back();
val.push_back(log10l(rs));
break;
case Symbol::log2:
sign.pop_back();
val.push_back(log2l(rs));
break;
case Symbol::max:
sign.pop_back();
val.push_back(rs);
max = false;
break;
case Symbol::min:
sign.pop_back();
val.push_back(rs);
min = false;
break;
case Symbol::rem:
sign.pop_back();
val.push_back(rs);
rem = false;
break;
case Symbol::sign:
sign.pop_back();
if (rs > 0) {
rs = 1;
} else if (rs < 0) {
rs = -1;
} else {
rs = 0;
}
val.push_back(rs);
break;
default:
apply(val, sign, rs);
break;
}
}
bool Calculator::error(std::string& err)
{
if (call != 0) {
set_err(Error::expression_error, "");
}
err = this->err;
return cont;
}
void Calculator::set_angle(bool radn)
{
radian = radn;
}
}
- strcase.h
/* using the string to the switch
* writen by Liangjin Song on 20200210
*/
#ifndef STRCASE_H
#define STRCASE_H
#include <cstdint>
namespace slj {
typedef std::uint64_t hash_t;
constexpr hash_t prime = 0x100000001B3ull;
constexpr hash_t basis = 0xCBF29CE484222325ull;
hash_t hash_(char const* str);
constexpr hash_t hash_compile_time(char const* str, hash_t last_value = basis)
{
return *str ? hash_compile_time(str + 1,
(*str ^ last_value) * prime)
: last_value;
}
constexpr hash_t operator"" _hash(char const* p, size_t)
{
return hash_compile_time(p);
}
}
#endif // STRCASE_H
- strcash.cpp
#include "strcase.h"
namespace slj {
hash_t hash_(char const* str)
{
hash_t ret{ basis };
while (*str) {
ret ^= *str;
ret *= prime;
str++;
}
return ret;
}
}
下一篇: 命令模式的应用之菜单项命令