I'm pretty confident in this code but I want to be sure I haven't missed anything to do with optimization or error handling. The program takes input in the form of {number} {operator} {number}
and prints the result. If the input is invalid it prints nothing.
Example input: 1.0 + 2.0
Output: 3
#include <iostream>
#include <string>
#include <stdexcept>
#include <regex>
double calculate(const double a, const char op, const double b)
{
switch (op)
{
case '+':
return a + b;
case '-':
return a - b;
case '*':
return a * b;
case '/':
if (b == 0.0)
throw std::invalid_argument {"Divide by zero"};
return a / b;
default:
throw std::invalid_argument {{op}};
}
}
int main()
{
const std::regex valid_input {
"([0-9]*\\.?[0-9]*) ([\\+\\-\\*\\/]) ([0-9]*\\.?[0-9]*)"};
std::string line;
std::smatch terms;
double a;
double b;
char op;
while (std::getline(std::cin, line))
{
if (!std::regex_match(line, terms, valid_input))
continue;
a = std::stod(terms[1]);
b = std::stod(terms[3]);
op = terms[2].str().front();
std::cout << calculate(a, op, b) << '\n';
}
}
2 Answers 2
Escaping escape characters becomes confusing.
Use the appropriate string literal.
// Your String
"([0-9]*\\.?[0-9]*) ([\\+\\-\\*\\/]) ([0-9]*\\.?[0-9]*)"
// A Raw String (no escape characters)
R"RAW(([0-9]*\.?[0-9]*) ([\+\-\*\/]) ([0-9]*\.?[0-9]*))RAW"
Don't think your regular expressions are good enough.
([0-9]*\.?[0-9]*)
// Can map
.
<blank>
I would do:
// If you have the decimal point then you require at least on digit after it.
// If there is no decimal point then you require at least one digit.
(([0-9]*(\.[0-9]+))|[0-9]+)
Don't think regular expressions are the best way to capture number operator number. The stream operators support this already so prefer to use those.
while (std::getline(std::cin, line))
{
std::stringstream lineStream(line);
double a;
double b;
char op;
char emptyTest;
if (lineStream >> a >> op >> b && !(lineStream >> emptyTest)) {
// Enter here if:
// 1: We correctly read a op and b
// 2: There are zero extra non-space characters on the line
// We find this because the read to emptyTest fails.
}
Declare your variables as close to the location that you use them. This also allows you to initialize them on declaration (which is always nice).
// Don't do this.
double a;
double b;
char op;
// Now you can see the type and know that they are initialized.
double a = std::stod(terms[1]);
double b = std::stod(terms[3]);
char op = terms[2].str().front();
Sure you can use a switch that is totally fine. But you can practice using the command pattern in this situation.
static std::map<char, std::function<double(double, doubel>> const actionMap = {
{'+', [](double lhs, double rhs){return lhs + rhs;}},
{'-', [](double lhs, double rhs){return lhs - rhs;}},
{'*', [](double lhs, double rhs){return lhs * rhs;}},
{'/', [](double lhs, double rhs){
if (b == 0.0) {
throw std::invalid_argument {"Divide by zero"};
}
return lhs / rhs;
}
};
// Now use the action map to find the function you want to call.
auto find = actionMap.find(op);
if (find == actionMap.end()) {
throw std::invalid_argument {{op}};
}
return find->second(a, b);
-
\$\begingroup\$ Thanks for the regex advice. After submitting the question I rewrote main to use stringstream and it works really good \$\endgroup\$Brady Dean– Brady Dean2017年07月20日 01:32:37 +00:00Commented Jul 20, 2017 at 1:32
- What happens if I enter only
+
surrounded by spaces? - The program should not abort if I enter
0 / 0
, but continue with the next line. - I'd like to omit the spaces around the operator.
- In the regular expression, the operator part can be written as
[+\\-*/]
.