lundi 2 août 2021

Elegant way to extract fields from a string with nested delimiters in C++11

Goal:
Given a string x in the form

<key1>=<value1>|<key2>=<value2>|...|,<key1>=<value1>|<key2>=<value2>|,...,,

where | is the delimiter for separating key-value pairs, , is the delimiter for separating a set of key-value pairs, and = is the delimiter for separating the key and value, extract the set of key pairs and map them to an index starting at 0 in the same order that they appear in the string.

Example:
x = "make=Honda|model=Civic|year=2011|,make=Acura|model=TL|year=20|,make=Lexus|model=ES|year=2021|,"

Result:
0 -> map containing make=Honda, model=Civic, year=2011
1 -> map containing make=Acura, model=TL, year=2007
2 -> map containing make=Lexus, model=ES, year=2021

Constraints:

  1. Must compile with C++11.
  2. Cannot create helper functions. I am restricted to only writing inside the int main() function.
  3. Cannot import external libraries. Has to be done only using the std namespace.

What I tried:

// vector of maps where each index is linked to a map of key pairs
std::vector<std::map<std::string, std::string>> list; 

// this is the input string
std::string s = fields.str();

std::string outerDelimiter = ",";
std::string innerDelimiter = "|";

size_t outerPos = 0;
std::string outer;

// outer loop that will try to find and split on the ',' delimiter
while ((outerPos = s.find(outerDelimiter)) != std::string::npos) {
    outer = s.substr(0, outerPos);
    
    size_t innerPos = 0;
    std::string keyPair;
    std::map<std::string, std::string> pairs;
    
    // inner loop that will try to find and split on the '|' delimiter
    while((innerPos = outer.find(innerDelimiter)) != std::string::npos) {
        int splitIndex = outer.find('=');
        std::string key = outer.substr(0, splitIndex);
        std::string value = outer.substr(splitIndex + 1);
        
        pairs[key] = value;
        
        outer.erase(outerPos, innerPos + innerDelimiter.length());
    }
    
    list.push_back(pairs);
    
    s.erase(0, outerPos + outerDelimiter.length());
}

I am able to solve the sub-problem of extracting all of the key pairs from a single set, but when I tried to introduce a second loop to try to repeat this process for multiple sets of key pairs, I get a segmentation fault.

Edit: I was able to solve my own problem:

string s = "make=Honda|model=Civic|year=2011|,make=Acura|model=TL|year=2007|,make=Lexus|model=ES|year=2021|,";
    
vector<map<string, string>> v;

string d1 = ",";
string d2 = "|";
string d3 = "=";
    
size_t i = 0;
while( (i = s.find(d1)) != string::npos ) {
    string segment = s.substr(0, i);
        
    map<string, string> pairs;
        
    size_t j = i;
    while( (j = segment.find(d2)) != string::npos ) {
        string pair = segment.substr(0, j);
            
        int splitIndex = pair.find(d3);
        string key = pair.substr(0, splitIndex);
        string value = pair.substr(splitIndex + 1);
            
        pairs[key] = value;
            
        segment.erase(0, j + d2.length());
    }
        
    v.push_back(pairs);
        
    s.erase(0, i + d1.length());
}

Follow-up Question:
Would this problem be easier to solve if the form of the input string was this instead?:

<key1>=<value2>|<key2>=<value2>|...,<key1>=<value1>|<key2>=<value2>|...

Example:
x = "make=Honda|model=Civic|year=2011,make=Acura|model=TL|year=20,make=Lexus|model=ES|year=2021"

Aucun commentaire:

Enregistrer un commentaire