1

I have a vector of string, vector <string> shapes holding these coordinates data:

Shape1, [3, 2]
Shape1, [6, 7]
Shape2, [7, 12, 3], [-9, 13, 68]
Shape1, [10, 3]
Shape2, [30, -120, 3], [-29, 1, 268]
Shape3, [15, 32], [1, 5]
Shape4, [24, 31, 56]

I am trying to cout the coordinates x and y from Shape1 and Shape3 and x, y, z from Shape2 and Shape4. This is a reproducible code:

#include <stdio.h>
#include <iostream>
#include <vector>
#include <string>

using namespace std;

int main()
{
    vector <string> shapes;
    
    shapes.push_back("Shape1, [3, 2]");
    shapes.push_back("Shape1, [632, 73]");
    shapes.push_back("Shape2, [7, 12, 3], [-9, 13, 68]");
    shapes.push_back("Shape1, [10, 3]");
    shapes.push_back("Shape2, [30, -120, 3], [-29, 1, 268]");
    shapes.push_back("Shape3, [15, 32], [1, 5]");
    shapes.push_back("Shape4, [24, 31, 56]");
    
    for(int i = 0; i < shapes.size(); i++)
    {
        // attempt to extract x
        size_t string_start = shapes[i].find(", [");
        string extracted = shapes[i].substr(string_start + 3, 1);
        
        cout << extracted << endl;
    }
    
    return 0;
}

As it is now, my current code can't cout the x properly - only the first character of x is cout. How should I handle the length of x? Subsequently, how should I cout the y and z in the data? The delimiter is , but there are multiples , everywhere.

user3118602
  • 403
  • 2
  • 10

3 Answers3

0

Since you already have the starting point of the x-coordinate, you can use this position to start finding the next ',' from there. e.g.

size_t string_start = shapes[i].find(", [");
size_t x_end = shapes[i].find_first_of(',', string_start + 3);

std::string parsed_x = shapes[i].substr(string_start, x_end - (string_start + 3));

This does not include the case when there is a Shape2, which has seemingly multiple x positions. But for this case you can just create a function which extracts the 'x' coordinate and let it run through you line multiple times.

Amachi
  • 102
  • 11
0

The below code is adapted from this stack overflow answer, go there to get the full explanation.

#include <stdio.h>
#include <iostream>
#include <vector>
#include <string>
#include <map>

using namespace std;

map<char, int> extract(string s) {
    map<char, int> r;
    size_t pos = 0;
    string token;
    char axes[] = {'x', 'y', 'z'};
    int count = 0;
    while ((pos = s.find(", ")) != string::npos) {
        token = s.substr(0, pos);
        r[axes[count++]] = stoi(token);
        s.erase(0, pos + 2); # ", ".length() == 2
    }
    r[axes[count]] = stoi(s);
    return r;
}

int main()
{
    vector <string> shapes;
    
    shapes.push_back("Shape1, [3, 2]");
    shapes.push_back("Shape1, [632, 73]");
    shapes.push_back("Shape2, [7, 12, 3], [-9, 13, 68]");
    shapes.push_back("Shape1, [10, 3]");
    shapes.push_back("Shape2, [30, -120, 3], [-29, 1, 268]");
    shapes.push_back("Shape3, [15, 32], [1, 5]");
    shapes.push_back("Shape4, [24, 31, 56]");

    
    size_t pos = 0;
    string token;
    for(int i = 0; i < shapes.size(); i++) {
        string s = shapes[i];
        while ((pos = s.find(", [")) != string::npos) {
            auto r = extract(s.substr(pos + 3, s.find("]") - (pos + 3)));  # ", [".length() == 3
            cout << "X: " << r['x'] << ", Y: " << r['y'] << (r.count('z') ? ", Z: " + to_string(r['z']) : "") << endl;
            s.erase(0, pos + 3); # ", [".length() == 3
        }
    }
    
    return 0;
}

Further improvement

The above code can be enhanced by having it store the extracted values in a Shape class or struct of some kind. This way, you'd have to do this operation once and work with the data as much times as you'd like. But, if your only goal is to print the data, then the above code suffices.

Fady Adal
  • 335
  • 1
  • 9
0

A different approach here, which I think is simpler, is to use regex pattern matching and searching. I think this will be more suitable to deal with the variables coordinate data and makes for easier handling of strings. std::regex_token_iterator can do what you need. It is (according to cppreference):

a read-only LegacyForwardIterator that accesses the individual sub-matches of every match of a regular expression within the underlying character sequence. It can also be used to access the parts of the sequence that were not matched by the given regular expression (e.g. as a tokenizer).

First of all, you can use a regex to get the coordinates in each shape string. The following regex will match against a sequence beginning with [ and ending with ], capturing the text within these characters:

std::regex reg(R"(\[(.+?)\])");

Then using the string extracted, we can tokenise the string into individual coordinates. Now we use a regex for the delimeter ", ", and pass -1 as the fourth parameter to std::sregex_token_iterator to get the text between them.

This function I think does what you need:

#include <iostream>
#include <regex>
#include <string>
#include <map>
#include <vector>

namespace
{
    std::map<int, std::string> lookup = { {0, "x"}, {1, "y"}, {2, "z"} };
}

void PrintShape(const std::string &shape)
{
    std::regex reg(R"(\[(.+?)\])");
    std::smatch mr;
    std::regex_search(shape, mr, reg);

    size_t string_start = shape.find(",");
    std::cout << shape.substr(0, string_start) << ":" << "\t";

    auto start = std::sregex_iterator(shape.begin(), shape.end(), reg);
    auto end = std::sregex_iterator{};

    for (std::sregex_iterator it = start; it != end; ++it)
    {
        //Get the first capturing group: [x, y, z]
        auto str = (*it)[1].str();

        //Tokenize group into x,y,z coordinates using delimiter ", "
        std::regex rgx(R"(, )");
        std::sregex_token_iterator iter(str.begin(), str.end(), rgx, -1);
        std::sregex_token_iterator iter_end{};

        //Print the coordinates
        int i = 0;
        std::cout << "[";
        for (; iter != iter_end; ++iter)
        {
            std::cout << lookup[i++] << " = " << *iter;

            if (std::next(iter) != iter_end)
            {
                std::cout << ", ";
            }
        }
        std::cout << "] ";
    }
    std::cout << "\n";
}

Here's a demo.

jignatius
  • 5,606
  • 2
  • 7
  • 21