2013-02-27 21 views
0

現在假設我必須使用字符串傳遞struct foo。該結構包含3件: 一個整型,一個float和一個字符串:C++使用字符串來存儲float和int

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

我決定做的是寫一個簡單的包裝進行編碼和解碼這個結構體富:

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? 
} 

我我只是好奇,如果有一個優雅的方式來做到這一點?

+2

爲什麼不使用JSON? – 2013-02-27 19:19:23

+0

查找['std :: to_string'](http://en.cppreference.com/w/cpp/string/basic_string/to_string),並使用分隔符。 – 2013-02-27 19:19:27

+0

我不想浪費更多的位......我的理解是,std :: to_string會使它更大,假設我的float是1.111111111,我只想花費4個字節。 – WhatABeautifulWorld 2013-02-27 19:22:57

回答

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

這樣做的優點是:

  • 它idiomaitc,一個衆所周知的圖案
  • 它也可以讓你輕鬆地打印對象的值,std::cout << f
1

考慮使用一個ASN.1 binary encoding like DER or PERProtocol Buffers。您可能還會發現this table of format comparisons useful

本質上這些標記數據爲「浮點,4字節」或「整數,8字節」,然後寫入二進制文件。這些格式是已知的和標準化的,以便實現可以在任何平臺上閱讀它們。

您可以將它們存儲在std::string中,因爲它實際上並不要求數據以空值終止。但是,如果數據包含空值,則字符串的c_str()函數將不起作用。

使用std::vector<unsigned char>來存儲字節會更容易混淆。

1

警告:以下代碼使用當前平臺的endian來混淆數據。如果您將此發送給其他可能沒有相同的endian和其他相關體系結構參數的平臺,請小心。

我打算假設你明白你在做什麼是將float的4個字節放到字符串的內存中,而不是float的字符串表示形式。例如,對於整數值爲2的字符串,將char值'\ 0','\ 0','\ 0','\ 2'放入字符串中。這與'002'作爲一個常規的人類可讀字符串(第一個是3個空終止符加上一個小數值爲2的字符)是不一樣的。你也是直接將float的二進制表示注入到字符串中。

如果這就是你想要的,那麼你最好使用除字符串以外的東西來存儲值(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++中的數字的二進制表示將皇家擰你的。

+0

你的意思是'vector.insert(vector.end(),adata,adata + sizeof(input.a));'和其他循環的類似替換,對吧? – Puppy 2013-02-27 19:51:43

+0

@DeadMG的確。我敢肯定,兩者都會產生同樣的效果,但我希望對OP做到明確。但是,將添加爲編輯。 – 2013-02-27 19:55:45