-3

So I have a string like this(all positive int, no spaces in square brackets):

"[(1,2),(10,4),(5,12),... ]"

And I would like extract int like this: 1,2,10,4,5,12. I'm now using the following code which does not handle int >=10

   std::istringstream coor(coorstring);
   std::vector<int> num;
   char c;
   while (coor.get(c)){
    if (isdigit(c)){
        unsigned int a = c - '0';
        if (a<max) {num.push_back(a);}
    }
   }

For example my current output is 1,2,1,0,4... rather than 1,2,10,4...

I'm curious what needs to be modified to get the output I want(without using regex)? And by the way, instead of using int vector, what is a good data structure to store these coordinates in c++ for a later use of drawing a graph?

genpfault
  • 51,148
  • 11
  • 85
  • 139
Grey258
  • 31
  • 3
  • 2
    in order to read "10" you need to read and process more than one character. – user4581301 Oct 19 '17 at 21:46
  • 1
    So basically you are asking "how to write a parser" - right? That's a solved problem (assuming you can construct a suitable grammar). The internet has all the needed information - one starting point might be [this](https://en.m.wikipedia.org/wiki/Recursive_descent_parser). Also lookup keywords like "lexer" and "tokenizer". – Jesper Juhl Oct 19 '17 at 21:49
  • Did you think about splitting the string in `(x,y)` parts, and then, split those into `x`/`y`? That would be the first idea that comes to mind. – Algirdas Preidžius Oct 19 '17 at 21:50
  • MAXIMUM OVER-[BOOST](https://en.wikipedia.org/wiki/Spirit_Parser_Framework)! – genpfault Oct 19 '17 at 21:51

5 Answers5

1

I would define some custom operator>> extraction operators to handle this:

typedef std::pair<int, int> point;
typedef std::vector<point> points;

std::istream& operator>>(std::istream &in, point &out)
{
    char ch1, ch2, ch3;
    if (in >> ch1 >> out.first >> ch2 >> out.second >> ch3)
    {
        if ((ch1 != '(') || (ch2 != ',') || (ch3 != ')'))
            in.setstate(std::ios_base::failbit);
    }
    return in;
}

std::istream& operator>>(std::istream &in, points &out)
{
    point pt;
    char ch;

    if (!(in >> ch))
        return in;

    if (ch != '[')
    {
        in.setstate(std::ios_base::failbit);
        return in;
    }

    ch = in.peek();
    do
    {
        if (ch == std::istream::traits_type::eof())
        {
            in.setstate(std::ios_base::failbit);
            break;
        }

        if (ch == ']')
        {
            in.ignore(1);
            break;
        }

        if (ch != '(')
        {
            in.setstate(std::ios_base::failbit);
            break;
        }

        if (!(in >> pt))
            break;

        out.push_back(pt);

        ch = in.peek();
        if (ch == ',')
        {
            in.ignore(1);
            ch = in.peek();
        }
    }
    while (true);

    return in;
}

std::istringstream iss(coorstring);
points coords;
iss >> coords;

live demo

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
0

Just use string splitting:

std::string s = "[(1,2),(10,4),(5,12),... ]";
std::string delimiter = ",";

while ((pos = s.find(delimiter)) != std::string::npos) {
    token = s.substr(0, pos);
    // deal with the data here
    s.erase(0, pos + delimiter.length());
}

It should output fairly regular data, all of which you know the format of:

"[(1", "2)", "(10", "4)", etc...
Austin A
  • 566
  • 2
  • 15
  • It would make it easier for you to just remove the '[' and ']' characters before doing the string splitting. – Austin A Oct 19 '17 at 21:52
  • 1
    A second solution would be to remove all characters that are not numeric or a comma. Then, split the string by a comma delimiter. (this is probably easier) – Austin A Oct 19 '17 at 21:54
  • Thx! Nice suggestion! I find an erase function like this: https://stackoverflow.com/questions/5891610/how-to-remove-certain-characters-from-a-string-in-c – Grey258 Oct 19 '17 at 22:01
0

You could use std::ignore(1000, '(') to skip everything until (and including) the next opening parentheses, and then use operator >> to read in the first coordinate, the comma, and the second coordinate.

Further, I'd suggest to use data structure pair to represent a pair of coordinates:

int main() {
    std::istringstream coor("[(1,2),(10,4),(5,12),... ]");
    std::vector<pair<int,int>> coordinates;

    for(;;) {
        coor.ignore(std::numeric_limits<std::streamsize>::max(), '(');
        int x,y;
        char c;
        if (!(coor >> x >> c >> y)) {
            break;
        }
        coordinates.push_back(pair<int,int>(x,y));            
    }

    for (auto coordinate : coordinates) {
        cout << coordinate.first << "/" << coordinate.second << endl;
    }
}

Note that this solution does not check the syntax of the input string, so it would accept also input without any closing parentheses or without the brackets. But if the syntax is guaranteed by other means, it is at least a simple solution.

Stephan Lechner
  • 34,891
  • 4
  • 35
  • 58
0

Not quite the solution, but what you should hear at this point:

Aside from tools and functions that already exist, when you encounter about any problem, begin with this: How do you yourself do it? What steps do you process in your mind to get from some symbols on a paper to concepts such as points? If you met somebody who has totally no idea how to read this, how would you explain it to him?

Also, think in sub-problems. Maybe start about a way to transform something like (8,1) into a point and then connect things. Don't be afraid to write dozens of separate functions that transform one thing into the other. Use functions that don't exist and then write them. Here is some code that might help you learn:

struct Point;

void remove_symbol(string& input, const char symbol);

Point parse_point(const string& input); //"(8,1)" -> Point on that coordinates

vector<string> delimit_by(const string& input, const char delimiter);

Didn't provide implementations on purpose - try to write all of them and ask again if it does not work out. Some basic hints, even if this is maybe not the most elegant code: You can iterate over the content of an integer by string::length() and the []-operator as with an array, and you can append things to a string by += (unelegant since costly, but easy for the start).

(There always might be direct ways to do those things using the STL, but writing them by yourself gives you some experience at the beginning, plus you can't know all of the STL. Just make sure to learn more about the STL in the long run.)

Aziuth
  • 3,652
  • 3
  • 18
  • 36
0

Simple two*-pass algorithm, producing a std::vector/whatever of points:

std::string s = "[(1,2),(10,4),(5,12),... ]";
std::replace_if( s.begin(), s.end(), []( char c ) { return !std::isdigit( c ); }, ' ' );
std::istringstream ss( s );
int x, y;
while (ss >> x >> y)
  points.emplace_back( point( x, y ) );

You can declare point any way you like, such as:

struct point
{
  int x, y;
  point(): x(), y() { }
  point( int x, int y ): x(x), y(y) { }
};

* Depending on the stringstream constructor, which may make another pass. This algorithm makes an explicit two passes.

Dúthomhas
  • 8,200
  • 2
  • 17
  • 39