0

ここで、string を使用して struct foo を渡す必要があるとします。構造体には、int、float、string の 3 つの部分が含まれています。

struct foo {
  int a;
  float b;
  string c;
}

私がやろうと決めたのは、この構造体 foo をエンコードおよびデコードする単純なラッパーを作成することです。

string& encode_foo(const foo &input) {
    // what do I do here?
    // declare a string and fill in 4 bytes for a, 4 bytes for b and then c?
    string ret;
    ret.append((char*)(&input.a), 4);  // This is really ugly, isn't it??
    ret.append((char*)(&input.b), 4);  // this is also very ugly??
}

foo decode_foo(const string &input) {
    // get input.c_str() and cast to int and float?
}

これを行うためのエレガントな方法があるかどうか、私はただ興味がありますか?

4

4 に答える 4

2

おそらく次のようなものです:

struct foo {
  int a;
  float b;
  string c;
}
std::ostream& operator<<(std::ostream& os, const foo& f) {
    return os << f.a << " " << f.b << " " << f.c;
}
std::istream& operator>>(std::istream& is, foo& f) {
    return is >> f.a >> f.b >> f.c;
}
std::string encode(const foo& f) {
     std::ostringstream oss;
     oss << f;
     return oss.str();
}
std::string decode(const std::string& s) {
     std::istringstream iss( s );
     foo f;
     iss >> f;
     return f;
}

int main() {
    foo f;
    std::string s=encode(f);
    f=decode(s);
}

これの利点は次のとおりです。

  • it idiomaitc、よく知られたパターン
  • また、オブジェクトの値を簡単に出力することもできます。 std::cout << f
于 2013-02-27T19:27:48.943 に答える
1

オプションは、文字列ストリームを使用して構造フィールドをエンコードおよびデコードすることです。これは単純な例です (しかし、より現実的なコードでは、スペースを含む文字列などに注意を払う必要があります):

#include <iostream>
#include <sstream>
#include <string>
using namespace std;

struct foo
{
    int a;
    float b;
    string c;
};

string encode_foo(const foo& f)
{
    ostringstream os;
    os << f.a << ' ' << f.b << ' ' << f.c;
    return os.str();
}

foo decode_foo(const string& s)
{
    istringstream is(s);
    foo f;
    is >> f.a;
    is >> f.b;
    is >> f.c;
    return f;
}

int main()
{
    foo f1;
    f1.a = 10;
    f1.b = 3.14f;
    f1.c = "hello";

    string s = encode_foo(f1);
    foo f2 = decode_foo(s);

    cout << f2.a << '\n' << f2.b << '\n' << f2.c << endl;
}

出力:

10
3.14
hello
于 2013-02-27T19:26:52.173 に答える
1

警告:次のコードは、現在のプラットフォームのエンディアンを使用してデータをいじっています。同じエンディアンやその他の関連するアーキテクチャ パラメータを持たない可能性のある他のプラットフォームにこれを送信する場合は注意してください。

floatの文字列表現ではなく、float の 4 バイトを文字列の memory に配置していることを理解していると仮定します。たとえば、値 2 の整数の場合、char 値 '\0'、'\0'、'\0'、'\2' を文字列に入れます。これは、'002' を通常の人間が読める文字列として書き出すことと同じではありません (最初の文字列は 3 つのヌル終了文字と 10 進値 2 の文字です)。また、フロートのバイナリ表現を文字列にも直接挿入しています。

それが必要な場合は、文字列以外のものを使用して値を格納することをお勧めします ( maybe a std::vector<char>/ std::vector<unsigned char>)。例えば:

std::vector<char>& encode_foo(const foo &input) {
    // Note that these loops, as @DeadMG pointed out in comments, can be
    // more easily accomplished with vector.insert( ... ), e.g.:
    // vector.insert(vector.end(), adata, adata + sizeof(input.a));
    std::vector<char> data;
    char* adata = (char*)&input.a;
    char* bdata = (char*)&input.b;
    char* cdata = (char*)input.c.data();
    for ( int i = 0; i < sizeof(input.a); ++i) {
        data.push_back( *adata );
        ++adata;
    }
    for ( int j = 0; j < sizeof(input.b); ++j) {
        data.push_back( *bdata );
        ++adata;
    }
    for ( int k = 0; k < input.c.length(); ++k) {
        data.push_back( *cdata );
        ++cdata;
    }

    // Now, data contains the absolute minimum binary 
    // representation of the structure
    // There are probably even simpler ways to do this, 
    // but the 3 loops are very explicit
    // And demonstrate what you want. 
    // You could consider std::copy or memcpy instead if you need
    // More flexibility.
    return data;
}

foo decode_foo(const std::vector<char>& input) {
    // Because you know the structure ahead of time, you can simply reverse the process
    // Here, I'll use memcpy to show how that's used too
    foo datafoo;
    memcpy( datafoo.a, input.data(), sizeof(datafoo.a) );
    // Offset by 4 (which is the typical size of an int
    memcpy( datafoo.b, input.data() + sizeof(datafoo.a), sizeof(datafoo.b) );
    // Now, we offset by 8, and each byte represents a character
    // We can memcpy into a std::string's data and null-terminate it as needed
    // By calling resize and telling it it's as big as the leftover data
    // minus the size of the other structures
    int offset = ( sizeof(datafoo.a) + sizeof(datafoo.b) );
    int csize = input.size() - offset;
    datafoo.c.resize( csize );
    memcpy( datafoo.c.input.data(), input.data() + offset, csize );
    // Usually, you don't use memcpy with strings, 
    // but this should do exactly as you want
    return datafoo;
}

あなたが要求したように、これは「バイトやスペースを無駄にする」べきではありませんがstd::vector<char>、バイナリ表現が必要な場合は、おそらくストレージとして使用する必要があることに注意してください。また、protobuffやその他のデータ パッキングおよびデータ転送プロトコルなども調べてください。上記の std::string も使用できますが、上記のいくつかの変更を加えて std::string を使用すると、その文字列が多くのプログラムやルーチンでうまく動作しなくstringsなることに注意してください。 C++ での数値の 2 進数表現は、それを台無しにしてしまいます。

于 2013-02-27T19:42:15.643 に答える
1

DER や PER などの ASN.1 バイナリ エンコーディング、またはProtocol Buffersの使用を検討してください。この形式比較の表も役立つ場合があります。

基本的に、これらはデータを「浮動小数点、4 バイト」または「整数、8 バイト」としてマークし、バイナリを書き込みます。形式は既知で標準化されているため、実装はどのプラットフォームでも読み取ることができます。

std::string実際にはデータを null で終了する必要がないため、これらを に格納できます。ただし、c_str()データに null が含まれている場合、文字列の機能は機能しません。

std::vector<unsigned char>a を使用してバイトを格納すると、混乱が少なくなります。

于 2013-02-27T19:34:55.200 に答える