考虑语法变化,从字符串中解析复数

时间:2019-01-29 03:12:14

标签: c++ string parsing stringstream

我正在制作一个控制台应用程序,可以在其中输入复数。一切正常,但我想在输入格式方面更加灵活。现在它只接受a + bi,我想这样做,所以如果我只输入3或-2i,它仍然可以工作。

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 };

据我所知,如果我添加以下内容

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

在我的最后一行之前,它将不起作用,因为>>通过了ss。 字符串中将没有空格。我不知道如何更改它而不只是重铸ss(我认为这不是最好的方法)

而且我不确定如何过滤掉将转换为int的任意随机字符并发送出去,检查'\ 0'感觉是错误的。

谢谢!

(顺便说一句,返回{true}会在被检查的类上转到m_fail,这是我能想到的最简单的错误处理方式,但是我是新来的,所以如果您有关于此主题的任何方便的文档我想了解更多)

2 个答案:

答案 0 :(得分:0)

我已经建议编写一个小型解析器(例如,基于SO: How to rearrange a string equation?中所示的语法图)。

下面,我按照OP尝试的方法做了一个更简单的方法(并且没有绘制语法图)。

即我的助手类型Complex的输入流运算符尝试读取两个数字。

如果第一个数字后跟一个i,则实际上是第二个数字,输入结束。

如果第二个数字后面没有i,则实际上不是第二个数字,而是丢失了,流将重置为之前的位置。

示例代码:

#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;
}

经过编译和测试:

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

在摆弄时,我仍然意识到一些弱点,例如由于1.23 + 45i+之间有空格,因此无法读取45。因此,即使使用更多代码,也可以肯定地对其进行改进。另一方面... OP没有指定应在何处接受空格,而在何处则不可以。

答案 1 :(得分:0)

恢复旧线程,但这是使用std :: complex和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");
}