Lior Dolinski
Lior Dolinski

Reputation: 1

Extracting a specific thing from a string

I have a string in the format <a,b>, which represents an edge in a directed graph (a is source and b is target). a and b are also strings themselves (for example, a can be "Square" and b is "Circle").

I need to build a function which extracts a, and another function which extracts b. So the signature will be:

string getSource(String edge); //will return b
string getTarget(String edge); //will return a

I am using the std::string library to represent those strings.

I know that I need to find a way to find the ',' separating them in the middle of the string, and get rid of the '<' and '>'. But I couldn't find a function in std::string that will help me to do that.

How would you go about on doing this?

Upvotes: 0

Views: 62

Answers (3)

Pete Becker
Pete Becker

Reputation: 76305

This sounds like it belongs in a class whose constructor takes that std::string argument and parses it.

class edge {
public:
    edge(const std::string& str);
    std::string source() const { return src; }
    std::string target() const { return tgt; }
private:
    std::string src;
    std::string tgt;
};

edge::edge(const std::string& str) {
    auto comma = std::find(std::begin(str), std::end(str), ',');
    if (str.length() < 3 || comma == std::end(str) || str.front() != '<' || str.back() != '>')
        throw std::runtime_error("bad input");
    src = std::string(std::next(std::begin(str)), comma);
    tgt = std::string(std::next(comma), std::prev(std::end(str)));
}

I wouldn't use a regular expression for such a simple parse. Regular expressions are expensive and highly overrated.

Upvotes: 0

cdhowie
cdhowie

Reputation: 169018

If you know for certain that the string is in the correct format, this is just a matter of using std::find to locate the characters of interest and then constructing a new string from those iterators. For example:

std::string getSource(std::string const & edge) {
    return {
        std::next(std::find(std::begin(edge), std::end(edge), '<')),
        std::find(std::begin(edge), std::end(edge), ',')
    };
}

std::string getTarget(std::string const & edge) {
    return {
        std::next(std::find(std::begin(edge), std::end(edge), ',')),
        std::find(std::begin(edge), std::end(edge), '>')
    };
}

If the strings are not in the correct format then these functions could exhibit undefined behavior. This could be fixed trivially with the use of a helper function:

template <typename T>
std::string checkedRangeToString(T begin, T end) {
    if (begin >= end) {
        // Bad format... throw an exception or return an empty string?
        return "";
    }

    return {begin, end};
}

std::string getSource(std::string const & edge) {
    return checkedRangeToString(
        std::next(std::find(std::begin(edge), std::end(edge), '<')),
        std::find(std::begin(edge), std::end(edge), ',')
    );
}

std::string getTarget(std::string const & edge) {
    return checkedRangeToString(
        std::next(std::find(std::begin(edge), std::end(edge), ',')),
        std::find(std::begin(edge), std::end(edge), '>')
    );
}

Upvotes: 0

cigien
cigien

Reputation: 60228

This seems to be a good use case for a regex:

std::regex sd {R"(<(.*),(.*)>)"};

and then your functions can be written as:

std::string getSource(std::string const & edge) {
  std::smatch m;
  std::regex_match(edge, m, sd);
  return m[1].str();
}

and in getTarget you would return m[2].str();.

Upvotes: 1

Related Questions