Parsing string in C++ with brackets

Asked by At

I have to parse string in C++ to get doubles out of it. String is in format like [(a,b)-(c,d)], where a, b, c and d are going to be double variables.

I was trying to use stringstrem, but it doesn't accept const char argument, so I have no idea right now how to solve it.

My code right now looks like:

ss >> "[(" >> a >> "," >> b >> ")-(" >> c >> "," >> d >> ")]";

But sadly it doesnt work :(

For example: [(1.2,3.4)-(6.5,7.4)]. And I want:

  a=1.2;
  b=3.4;
  c=6.5;
  d=7.4;

3 Answers

6
Fire Lancer On Best Solutions

With input streams, you need to read input to a variable. You could read into a char or into a string with std::istream::read. Ideally you would check the characters are actually as expected, to catch and probably reject say [email protected]#~6.5,7.8++

char chr;
ss >> chr >> chr >> a >> chr >> b >> chr >> chr >> chr >> c >> chr >> d >> chr >> chr;

Alternatively you might read it as a string, and then you can use the C++ <regex> functionality to match it, perhaps something like:

^\[\((\d+\.\d+),(\d+\.\d+)\)-\((\d+\.\d+),(\d+\.\d+)\)\]$

This will give you 4 capture groups on success, which you can then convert to doubles. You can modify this if for example extra spaces are allowed, the decimal point is optional, etc.

If there is any variation in the format at all, for example sometimes you get 3 numbers, e.g. [(1.2,3.4)-(6.5,7.4)+(1.5,2)] you would need to do more in depth parsing than the >> operator can directly support.

Possibly with logic based on what ss >> chr read, or by looping on a regex based on where the previous match ended (instead of matching the entire string at once) although things get a lot more advanced.

1
Ozzy On

It is not the most elegant way to solve it for sure, but it is a way that humans can read and maintain and possibly extend easily later... I would prefer this over regular expressions :-)

        auto input = "[(1.0,2.0)-(3.0,444.555)]";
        std::string in(input);
        for (auto iter = in.begin(); iter < in.end(); iter++)
        {
          char c = *iter;
          // If c == '(' or ',' parse a double number (digits or '.')
          // and save it somewhere. 

          // else: stop parsing a number / continue
        }
1
Lightness Races in Orbit On

Honestly, this is one of those times when the C library still has the best tool for the job:

#include <cstdio>
#include <string>
#include <iostream>

int main()
{
    const std::string input = "[(1.2,3.4)-(6.5,7.4)]";

    double a, b, c, d;
    if (std::sscanf(input.c_str(), "[(%lf,%lf)-(%lf,%lf)]", &a, &b, &c, &d) == 4)
    {
        std::cout << "a=" << a << '\n';
        std::cout << "b=" << b << '\n';
        std::cout << "c=" << c << '\n';
        std::cout << "d=" << d << '\n';
    }
    else
    {
        std::cout << "Did not parse!\n";
    }
}

// Output:
// 
//   a=1.2
//   b=3.4
//   c=6.5
//   d=7.4

(live demo)

IOStreams can do it (as Fire Lancer showed) but reeeeally verbosely, and you can roll it yourself (as Ozzy showed) but why go to that effort?

There are also vast Boost.Spirit monstrosities that can do it in a "modern" way for very little gain.

Really this is a one-line solved problem.