c++使用string来存储float和int

c++ use string to store float and int

本文关键字:float int 存储 使用 string c++      更新时间:2023-10-16

假设现在我必须使用string传递一个结构体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?
}

我只是好奇是否有一种优雅的方式来做到这一点?

可能是这样的:

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

可以选择使用字符串流对结构字段进行编码和解码。这是一个简单的示例(但在更实际的代码中,您应该注意包含空格的字符串等内容):

#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

考虑使用ASN.1二进制编码,如DER或PER或协议缓冲区。您可能还会发现这个格式比较表很有用。

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

您可以将它们存储在std::string中,因为它实际上不需要数据以null终止。然而,字符串的c_str()函数将不工作,如果数据包含null。

使用std::vector<unsigned char>存储字节会更容易理解。

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

我将假设您理解您正在做的是将浮点数的4个字节放入字符串的内存,而不是浮点数的字符串表示。例如,对于值为2的整数,您将字符值'','','','2'放入字符串中。这与将'002'写成普通的人类可读字符串不同(第一个字符串是3个null终止符加上一个十进制值为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++中数字的二进制表示将为您带来麻烦。