1

I'm making a console application where I can input complex numbers. Everything works, but I'd like to be more flexible with the input formatting. Right now it only accepts a+bi, I'd like to make it so if I just enter 3 or -2i, it still works.

Complex console::parseComplex(std::string cmp) { 
    float a, b;
    char i;
    std::stringstream ss(cmp);
    if (ss >> a >> b >> i && i == 'i')
        return { a, b };
    else return { true };

The thing is that as far as I know if I add the following

    else if (ss >> a >> i && i == '\0')
        return { a, 0 };
    else if (ss >> b >> i && i == 'i')
        return { 0, i };

Before my last line, it won't work, since >> goes through the ss. There won't be any spaces in the string. I'm not sure how to change it without just recasting the ss (which I assume is not the best way of doing it)

And I'm not sure how to filter out any random characters being converted into ints and send away, checking for '\0' feels wrong.

Thanks!

(BTW the return {true} goes to m_fail on the class which get's checked, it's the simplest way of error handling i could come up with, but I'm new to this so if you have any doc handy on the topic I'd love to read more)

Sid S
  • 5,887
  • 2
  • 12
  • 23
Mauro
  • 73
  • 4
  • What you need is a little parser. If you like you may have a look at [SO: How to rearrange a string equation?](https://stackoverflow.com/a/50021308/7478597) for a similar Q/A. – Scheff's Cat Jan 29 '19 at 06:34

2 Answers2

0

I already recommended to write a small parser (e.g. based on a syntax diagram like shown in SO: How to rearrange a string equation?).

In the following, I made a simpler approach following the way tried by OP (and without sketching a syntax diagram).

I.e. the input stream operator for my helper type Complex tries to read two numbers.

If first number is followed by an i this is actually the second number and input ends.

If second number is not followed by an i it's actually not the second but missing and stream is reset to the position before.

The sample code:

#include <cmath>
#include <iomanip>
#include <iostream>
#include <sstream>

struct Complex {
  float re, im;
};

std::istream& operator >> (std::istream &in, Complex &value)
{
  value.im = 0;
  // read 1st value
  if (!(in >> value.re)) return in; // ERROR!
  // check whether next char is 'i'
  char c; if (!(in >> c)) return in; // End of Input
  if (c == 'i') {
    value.im = value.re; value.re = 0.0;
    return in;
  }
  in.unget();
  // remember start of imaginary
  std::streampos pos = in.tellg();
  if (!(in >> value.im)) return in; // ERROR or end of input
  if (!(in >> c) || c != 'i') { // ERROR or premature end of input
    value.im = 0.0;
    in.clear();
    in.seekg(pos);
    return in;
  }
  return in;
}

std::ostream& operator << (std::ostream &out, const Complex &value)
{
  return out << value.re << (std::signbit(value.im) ? "" : "+") << value.im << 'i';
}

int main()
{
  // samples:
  std::string samples[] = {
    "1.23+45i",
    " 1.23 +45i",
    " 1.23E3 +45e-1i",
    "Completely wrong!",
    // multiple numbers in one line
    "1.23+45i 1.23 +45i 1.23E3 +45e-1i",
    "1.23 2.34-1i -2i"
  };
  int line = 1;
  for (const std::string &sample : samples) {
    std::cout << "Parse line " << line++ << " '" << sample << "':\n";
    std::istringstream in(sample);
    std::cout << "Got:";
    Complex value;
    while (in >> std::skipws >> value) std::cout << ' ' << value;
    std::cout << '\n'
      << "Stopped with "
      << (in.eof() ? "End of Input" : !in.good() ? "Error" : "???")
      << '\n';
  }
  return 0;
}

Compiled and tested:

g++ (GCC) 8.1.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Parse line 1 '1.23+45i':
Got: 1.23+45i
Stopped with End of Input
Parse line 2 ' 1.23 +45i':
Got: 1.23+45i
Stopped with End of Input
Parse line 3 ' 1.23E3 +45e-1i':
Got: 1230+4.5i
Stopped with End of Input
Parse line 4 'Completely wrong!':
Got:
Stopped with Error
Parse line 5 '1.23+45i 1.23 +45i 1.23E3 +45e-1i':
Got: 1.23+45i 1.23+45i 1230+4.5i
Stopped with End of Input
Parse line 6 '1.23 2.34-1i -2i':
Got: 1.23+0i 2.34-1i 0-2i
Stopped with End of Input

Live Demo on coliru

While fiddling I still realized some weaknesses like e.g. 1.23 + 45i cannot be read because of space between + and 45. So, with even more code this could be improved for sure. On the other hand... OP didn't specify where spaces should be accepted and where not.

Scheff's Cat
  • 16,517
  • 5
  • 25
  • 45
  • Wow, thank you for taking the time to do this! The user's input is tokenized beforehand to check for other commands, so I'm parsing strings without spaces. 45 or -3i should equal 45+0i, so this works as intended. Thank you! – Mauro Jan 29 '19 at 13:32
0

Reviving an old thread, but here's a more concise alternative that uses std::complex and std::regex:

#include <stdexcept>
#include <complex>
#include <regex>
#include <string>

std::complex<double> ParseComplex(std::string const &complex_str)
{
    std::regex complex_regex("^([+-]?[0-9]*\\.?[0-9]+([eE][+-]?[0-9]+)?)[+-]([+-]?[0-9]*\\.?[0-9]+([eE][+-]?[0-9]+)?)?(i)$");
    std::regex real_imag_regex("^([+-]?[0-9]*\\.?[0-9]+([eE][+-]?[0-9]+)?)(i)?$");

    std::smatch complex_match;
    std::smatch real_imag_match;

    if (std::regex_search(std::cbegin(complex_str), std::cend(complex_str), complex_match, complex_regex)) {
        return std::complex<double>(std::stod(complex_match[1].str()), complex_match[4].str().empty() ? 1.0 : std::stod(complex_match[4].str()));
    }
    else if (std::regex_search(std::cbegin(complex_str), std::cend(complex_str), real_imag_match, real_imag_regex)) {
        if (!real_imag_match[3].str().empty()) {
            return std::complex<double>(0.0, std::stod(real_imag_match[1].str()));
        }
        else {
            return std::complex<double>(std::stod(real_imag_match[1].str()), 0.0);
        }
    }

    throw std::invalid_argument("Does not contain a complex number");
}
Tannz0rz
  • 100
  • 1
  • 6