0

假设现在我必须使用字符串传递一个 struct foo。该结构包含 3 个部分:一个 int、一个 float 和一个 string:

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

这样做的好处是:

  • 它成语,众所周知的模式
  • 它还允许您轻松打印对象的值, std::cout << f
于 2013-02-27T19:27:48.943 回答
1

研究使用ASN.1 二进制编码,如 DER 或 PERProtocol Buffers。您可能还会发现此格式比较表很有用

本质上,这些将数据标记为“浮点,4 字节”或“整数,8 字节”,然后写入二进制文件。这些格式是已知的和标准化的,因此实现可以在任何平台上读取它们。

您可以将它们存储在 a 中,std::string因为它实际上并不要求数据以空值结尾。但是,如果数据包含空值,则字符串的c_str()功能将不起作用。

std::vector<unsigned char>使用 a存储字节会更容易混淆。

于 2013-02-27T19:34:55.200 回答
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

警告:以下代码使用当前平台的字节序来处理数据。如果您将其发送到可能没有相同字节序和其他相关架构参数的其他平台,请小心。

我将假设您了解您正在做的是将浮点数的 4 个字节放入字符串的内存中,而不是浮点数的字符串表示。例如,对于值为 2 的整数,您将 char 值 '\0'、'\0'、'\0'、'\2' 放入字符串中。这与将 '002' 写为常规的人类可读字符串不同(第一个是 3 个空终止符加上一个十进制值为 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预计为 null-终止,并且 C++ 中数字的二进制表示将为您彻底解决这个问题。

于 2013-02-27T19:42:15.643 回答