разбить строку данных на вектор структур? - PullRequest
2 голосов
/ 28 марта 2011

Итак, у меня есть следующая строка данных, получаемых через TCP-соединение winsock, и я хотел бы выполнить расширенную токенизацию в вектор структур, где каждая структура представляет одну запись.

std::string buf = "44:william:adama:commander:stuff\n33:luara:roslin:president:data\n"

struct table_t
{
    std::string key;
    std::string first;
    std::string last;
    std::string rank;
    std::additional;
};

Каждая запись в строке отделяется переводом каретки.Моя попытка разделить записи, но еще не разделить поля:

    void tokenize(std::string& str, std::vector< string >records)
{
    // Skip delimiters at beginning.
    std::string::size_type lastPos = str.find_first_not_of("\n", 0);
    // Find first "non-delimiter".
    std::string::size_type pos     = str.find_first_of("\n", lastPos);
    while (std::string::npos != pos || std::string::npos != lastPos)
    {
        // Found a token, add it to the vector.
        records.push_back(str.substr(lastPos, pos - lastPos));
        // Skip delimiters.  Note the "not_of"
        lastPos = str.find_first_not_of("\n", pos);
        // Find next "non-delimiter"
        pos = str.find_first_of("\n", lastPos);
    }
}

Кажется совершенно ненужным повторять весь этот код еще раз для дальнейшего токенизации каждой записи через двоеточие (внутренний разделитель полей) вструктура и толкать каждую структуру в вектор.Я уверен, что есть лучший способ сделать это, или, возможно, дизайн сам по себе неправильный.

Спасибо за любую помощь.

Ответы [ 2 ]

2 голосов
/ 28 марта 2011

Мое решение:

struct colon_separated_only: std::ctype<char> 
{
    colon_separated_only(): std::ctype<char>(get_table()) {}

    static std::ctype_base::mask const* get_table()
    {
        typedef std::ctype<char> cctype;
        static const cctype::mask *const_rc= cctype::classic_table();

        static cctype::mask rc[cctype::table_size];
        std::memcpy(rc, const_rc, cctype::table_size * sizeof(cctype::mask));

        rc[':'] = std::ctype_base::space; 
        return &rc[0];
    }
};

struct table_t
{
    std::string key;
    std::string first;
    std::string last;
    std::string rank;
    std::string additional;
};

int main() {
        std::string buf = "44:william:adama:commander:stuff\n33:luara:roslin:president:data\n";
        stringstream s(buf);
        s.imbue(std::locale(std::locale(), new colon_separated_only()));
        table_t t;
        std::vector<table_t> data;
        while ( s >> t.key >> t.first >> t.last >> t.rank >> t.additional )
        {
           data.push_back(t);
        }
        for(size_t i = 0 ; i < data.size() ; ++i )
        {
           cout << data[i].key <<" ";
           cout << data[i].first <<" "<<data[i].last <<" ";
           cout << data[i].rank <<" "<< data[i].additional << endl;
        }
        return 0;
}

Вывод:

44 william adama commander stuff
33 luara roslin president data

Онлайн-демонстрация: http://ideone.com/JwZuk


Техника, которую я здесь использовал, описана в моемдругое решение другого вопроса:

Элегантные способы подсчета частоты слов в файле

1 голос
/ 28 марта 2011

Для разбивки строки на записи я бы использовал istringstream хотя бы потому, что это упростит изменения позже, когда я захочу прочитать из файла.Для токенизации наиболее очевидным решением является boost :: regex, поэтому:

std::vector<table_t> parse( std::istream& input )
{
    std::vector<table_t> retval;
    std::string line;
    while ( std::getline( input, line ) ) {
        static boost::regex const pattern(
            "\([^:]*\):\([^:]*\):\([^:]*\):\([^:]*\):\([^:]*\)" );
        boost::smatch matched;
        if ( !regex_match( line, matched, pattern ) ) {
            //  Error handling...
        } else {
            retval.push_back(
                table_t( matched[1], matched[2], matched[3],
                         matched[4], matched[5] ) );
        }
    }
    return retval;
}

(я предположил логический конструктор для table_t. Также: в C существует очень давняя традиция, что имена, заканчивающиеся на _t, являютсяtypedef, так что вам, вероятно, лучше найти другое соглашение.)

...