Search code examples
c++data-structurestokenize

Tokenize a std::string to a struct


Let's say I have the following string that I want to tokenize as per the delimiter '>':

std::string veg = "orange>kiwi>apple>potato";

I want every item in the string to be placed in a structure that has the following format:

struct pack_item
{
    std::string it1;
    std::string it2;
    std::string it3;
    std::string it4;
};

I know how to do it this way:

pack_item pitem;

std::stringstream veg_ss(veg);
std::string veg_item;

std::getline(veg_ss, veg_item, '>')
pitem.it1 = veg_item;
std::getline(veg_ss, veg_item, '>')
pitem.it2 = veg_item;
std::getline(veg_ss, veg_item, '>')
pitem.it3 = veg_item;
std::getline(veg_ss, veg_item, '>')
pitem.it4 = veg_item;

Is there a better and one-liner kind of way to do it?


Solution

  • You don't need an intermediate variable.

    pack_item pitem;
    
    std::stringstream veg_ss(veg);
    
    std::getline(veg_ss, pitem.it1, '>');
    std::getline(veg_ss, pitem.it2, '>');
    std::getline(veg_ss, pitem.it3, '>');
    std::getline(veg_ss, pitem.it4, '>');
    

    You might want to make that a function, e.g. operator >> (with a similar operator <<)

    std::istream& operator >>(std::istream& is, pack_item & pitem) {
        std::getline(is, pitem.it1, '>');
        std::getline(is, pitem.it2, '>');
        std::getline(is, pitem.it3, '>');
        std::getline(is, pitem.it4, '>');
        return is;
    }
    
    std::ostream& operator <<(std::ostream& os, pack_item & pitem) {
        return os << pitem.it1 << '>'
                  << pitem.it2 << '>'
                  << pitem.it3 << '>'
                  << pitem.it4 << '>';
    }
    
    int main() {
        std::stringstream veg_ss("orange>kiwi>apple>potato>");
        pack_item pitem;
        veg_ss >> pitem;
    }
    

    Is there a better and one-liner kind of way to do it?

    You can make a type who's >> reads in a string up to a delimiter, and read all four elements in one statement. Is that really "better"?

    template <bool is_const>
    struct delimited_string;
    
    template<>
    struct delimited_string<true> {
        const std::string & string;
        char delim;
    };
    
    template<>
    struct delimited_string<false> {
        std::string & string;
        char delim;
    };
    
    delimited_string(const std::string &, char) -> delimited_string<true>;
    delimited_string(std::string &, char) -> delimited_string<false>;
    
    std::istream& operator >>(std::istream& is, delimited_string<false> s) {
        return std::getline(is, s.string, s.delim);
    }
    
    template <bool is_const>
    std::ostream& operator <<(std::ostream& os, delimited_string<is_const> s) {
        return os << s.string << s.delim;
    }
    
    std::istream& operator >>(std::istream& is, pack_item & pitem) {
        return is >> delimited_string { pitem.it1, '>' }
                  >> delimited_string { pitem.it2, '>' }
                  >> delimited_string { pitem.it3, '>' }
                  >> delimited_string { pitem.it4, '>' };
    }
    
    std::ostream& operator <<(std::ostream& os, const pack_item & pitem) {
        return os << delimited_string { pitem.it1, '>' }
                  << delimited_string { pitem.it2, '>' }
                  << delimited_string { pitem.it3, '>' }
                  << delimited_string { pitem.it4, '>' };
    }