# Converting a string to a list of key-value pairs

I'm trying to convert a string (here after called input) into a vector of key-value pairs, all in C++03 (cannot use C++11, cannot use Boost libs).

The string in question can contain whitespaces. A comma (,) delimits key-value pairs and the equals sign (=) separates the key from the value. A sample string (also in the code could be: "ab=xy z,time a=1s". Notice the lack of a final ,.

Any improvement is most welcome.

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

struct Flag_name_value {
std::string name;
std::string value;
};

std::istringstream& operator>>(std::istringstream& iss, Flag_name_value& fnv)
{
char ch;
std::ostringstream oss;

while (iss >> std::noskipws >> ch) {
std::cout << ch << "\n";
switch(ch) {
case('='):
fnv.name = oss.str();
oss.str("");
break;
case(','):
fnv.value = oss.str();
oss.str("");
break;
default:
oss << ch;
break;
}

if (!fnv.name.empty() && !fnv.value.empty())
{
return iss;
}
}

// We end here if the string didn't end with ','
fnv.value = oss.str();
return iss;
}

int main () {

std::string input("ab=xy z,time a=1s");

std::istringstream iss(input);

std::vector<Flag_name_value> flags;
while (true) {
Flag_name_value f;
iss >> f;
flags.push_back(f);
if (!iss) break;
}

return 0;

}


First, you can use std::getline to extract key-value pairs from the original string. This can be done by passing , as a delimiter to std::getline:

while (std::getline(iss, token, ',')) { ... }


Notice that you don't have to perform any checks on the stream this way. Once std::getline will reach the end of the stream it will set the eofbit on it, so the loop condition will evaluate to false on next iteration.

Next, use can use std::string::find to find an index of the occurrence of the = character in strings filled by std::getline:

size_t pos = token.find('=');


After that use can use this index to extract two substrings - your key and your value:

flag.name    = token.substr(0, pos);
flag.value   = token.substr(pos + 1);


Notice that you can omit the second argument of std::string::substr, since it has the default value of std::string::npos. This value is very huge (in fact, it equals to the maximum value that can be represented by a size type of std::string), but std::string::substr never reads past the end of line, so everything will be good.

Tie all this together and you'll get a small and readable solution:

std::string input("ab=xy z,time a=1s");
std::istringstream iss(input);

std::vector<Flag_name_value> flags;

std::string token;
Flag_name_value flag;
while (std::getline(iss, token, ',')) {
size_t pos   = token.find('=');
flag.name    = token.substr(0, pos);
flag.value   = token.substr(pos + 1);
flags.push_back(flag);
}

• Will throw std::out_of_range if any extracted token does not contain an =-sign. – Deduplicator Oct 18 '15 at 0:04
• It will throw std::out_of_range if the first argument (that is a starting position) is larger than the size of the string. If extracted string does not contain the = character, std::string::find will return std::string::npos, which is obviously larger than the size of the string. – Nikita Kakuev Oct 18 '15 at 0:07
• Yes. Just though you might want to note that down. – Deduplicator Oct 18 '15 at 0:35
• That's just awesome. Short, yet terse and really easy to understand. Thanks! – Jir Oct 18 '15 at 19:55