2

したがって、TCP winsock 接続を介して受信されている次のデータ文字列があり、各構造体が 1 つのレコードを表す構造体のベクトルに高度なトークン化を行いたいと考えています。

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);
    }
}

コロン (内部フィールドセパレーター) を介して各レコードをさらにトークン化し、各構造体をベクターにプッシュするために、そのすべてのコードをもう一度繰り返す必要はまったくないようです。これを行うためのより良い方法があると確信しているか、設計自体が間違っている可能性があります。

助けてくれてありがとう。

4

2 に答える 2

2

私の解決策:

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


ここで使用した手法は、別の質問に対する別の解決策で説明されています。

ファイル内の単語の頻度を数える洗練された方法

于 2011-03-28T17:45:27.063 に答える
1

文字列をレコードに分割するには、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 の論理コンストラクターを想定しています。また、_t で終わる名前は typedef であるという非常に長い伝統が C にあるため、おそらく他の規則を見つけた方がよいでしょう。)

于 2011-03-28T16:41:16.633 に答える