我想从一个数字中得到一个固定长度的字符串,就像python中的struct.pack一样,但是在c++中。我想到了itoa (i,buffer,2),但是问题是它的长度取决于平台。有没有办法让它独立于平台?
struct.pack
itoa (i,buffer,2)
mspsb9vt1#
如果你正在寻找一个类似于Python的struct包的完整解决方案,你可以看看Google's Protocol Buffers Library,使用它可以解决很多问题(例如字节序、语言可移植性、跨版本兼容性)。
ve7v8dk22#
这是一个开始:
typedef std::vector<uint8_t> byte_buffer; template <std::size_t N> void append_fixed_width(byte_buffer& buf, uintmax_t val) { int shift = ((N - 1) * 8); while (shift >= 0) { uintmax_t mask = (0xff << shift); buf.push_back(uint8_t((val & mask) >> shift)); shift -= 8; } } template <typename IntType> void append_bytes(byte_buffer& buf, IntType val) { append_fixed_width<sizeof(IntType)>(buf, uintmax_t(val)); } int main() { // usage example byte_buffer bytes; append_bytes(bytes, 1); // appends sizeof(int) bytes append_bytes(bytes, 1ul); // appends sizeof(unsigned long) bytes append_bytes(bytes, 'a'); // appends sizeof(int) bytes :p append_bytes(bytes, char('a')); // appends 1 byte return 0; }
Append_bytes会将任何整数类型追加到使用std::vector<uint8_t>表示的字节缓冲区中。值以big endian字节顺序打包。如果需要更改此设置,请调整append_fixed_width以不同顺序遍历值。这些函数构建了一个原始字节缓冲区,所以解码它的人有责任知道里面有什么。IIRC,这也是struct.pack所做的;换句话说,struct.unpack的调用者需要提供相同的格式字符串,您可以编写append_fixed_width的变体来打包TLV:
Append_bytes
std::vector<uint8_t>
append_fixed_width
struct.unpack
template <typename TagType, typename ValueType> void append_tlv(byte_buffer& buf, TagType t, ValueType val) { append_fixed_width<sizeof(TagType)>(buf, uintmax_t(t)); append_fixed_width<sizeof(std::size_t)>(buf, uintmax_t(sizeof(ValueType))); append_fixed_width<sizeof(ValueType)>(buf, uintmax_t(val)); }
我会认真考虑Jeremy的建议,我希望在我编写现在所有的二进制打包代码时它就已经存在了。
h9a6wy2h3#
您需要通过typedef定义一个精确宽度的整数类型;如果使用C99,则int16_t是在<stdint.h>中预定义的。然后可以强制转换为该类型,并键入变量的内存表示:
int16_t
<stdint.h>
int16_t val = (int16_t) orig_val; void *buf = &val;
注意,您仍然需要处理字节序。如果你没有C99,你可以使用编译时或运行时大小测试。对于编译时测试,考虑使用autoconf,它已经计算了各种基元类型的大小,这样你就可以在编译时选择一个好的类型。在运行时,只需进行一系列sizeof测试。注意,这在运行时有点不合适。因为测试总是得到相同的结果。作为autoconf的替代方法,您也可以使用编译器/系统标识宏进行编译时测试。
8qgya5xd4#
C++的方法是使用stringstream:
stringstream
stringstream ss; int number=/*your number here*/; ss<<number;
要获得缓冲区,可以使用ss.str().c_str()。
ss.str().c_str()
62o28rlo5#
我在c/c中进行了此实现,以比较python/php/dart/chttps://github.com/dart-lang/sdk/issues/50708之间的pack函数的执行时间
#include <span> #include <vector> #include <cstdio> #include <cstdint> #include <iomanip> #include <iostream> #include "time.h" #include <map> #define STRUCT_ENDIAN_NOT_SET 0 #define STRUCT_ENDIAN_BIG 1 #define STRUCT_ENDIAN_LITTLE 2 static int myendian = STRUCT_ENDIAN_NOT_SET; void debug_print2(const char *str, std::vector<unsigned char> vec) { std::cout << str; for (auto i : vec) std::cout << i; std::cout << "\r\n"; } int struct_get_endian(void) { int i = 0x00000001; if (((char *)&i)[0]) { return STRUCT_ENDIAN_LITTLE; } else { return STRUCT_ENDIAN_BIG; } } static void struct_init(void) { myendian = struct_get_endian(); } static void pack_int16_t(unsigned char **bp, uint16_t val, int endian) { if (endian == myendian) { *((*bp)++) = val; *((*bp)++) = val >> 8; } else { *((*bp)++) = val >> 8; *((*bp)++) = val; } } static void pack_int32_t(unsigned char **bp, uint32_t val, int endian) { if (endian == myendian) { *((*bp)++) = val; *((*bp)++) = val >> 8; *((*bp)++) = val >> 16; *((*bp)++) = val >> 24; } else { *((*bp)++) = val >> 24; *((*bp)++) = val >> 16; *((*bp)++) = val >> 8; *((*bp)++) = val; } } static void pack_int64_t(unsigned char **bp, uint64_t val, int endian) { if (endian == myendian) { *((*bp)++) = val; *((*bp)++) = val >> 8; *((*bp)++) = val >> 16; *((*bp)++) = val >> 24; *((*bp)++) = val >> 32; *((*bp)++) = val >> 40; *((*bp)++) = val >> 48; *((*bp)++) = val >> 56; } else { *((*bp)++) = val >> 56; *((*bp)++) = val >> 48; *((*bp)++) = val >> 40; *((*bp)++) = val >> 32; *((*bp)++) = val >> 24; *((*bp)++) = val >> 16; *((*bp)++) = val >> 8; *((*bp)++) = val; } } static int pack(void *b, const char *fmt, long long *values, int offset = 0) { unsigned char *buf = (unsigned char *)b; int idx = 0; const char *p; unsigned char *bp; int ep = myendian; int endian; bp = buf + offset; auto bpp = &bp; if (STRUCT_ENDIAN_NOT_SET == myendian) { struct_init(); } for (p = fmt; *p != '\0'; p++) { auto value = values[idx]; switch (*p) { case '=': // native ep = myendian; break; case '<': // little-endian endian = STRUCT_ENDIAN_LITTLE; ep = endian; break; case '>': // big-endian endian = STRUCT_ENDIAN_BIG; ep = endian; break; case '!': // network (= big-endian) endian = STRUCT_ENDIAN_BIG; ep = endian; break; case 'b': *bp++ = value; break; case 'c': *bp++ = value; break; case 'i': if (ep == STRUCT_ENDIAN_LITTLE) { *bp++ = value; *bp++ = value >> 8; *bp++ = value >> 16; *bp++ = value >> 24; } else { *bp++ = value >> 24; *bp++ = value >> 16; *bp++ = value >> 8; *bp++ = value; } break; case 'h': if (ep == STRUCT_ENDIAN_LITTLE) { *bp++ = value; *bp++ = value >> 8; } else { *bp++ = value >> 8; *bp++ = value; } break; case 'q': if (ep == STRUCT_ENDIAN_LITTLE) { *bp++ = value; *bp++ = value >> 8; *bp++ = value >> 16; *bp++ = value >> 24; *bp++ = value >> 32; *bp++ = value >> 40; *bp++ = value >> 48; *bp++ = value >> 56; } else { *bp++ = value >> 56; *bp++ = value >> 48; *bp++ = value >> 40; *bp++ = value >> 32; *bp++ = value >> 24; *bp++ = value >> 16; *bp++ = value >> 8; *bp++ = value; } break; } idx++; } return (bp - buf); } int main() { time_t start, end; time(&start); // std::ios_base::sync_with_stdio(false); std::vector<unsigned char> myVector{}; myVector.reserve(100000000 * 16); for (int i = 0; i < 100000000; i++) // 100000000 { char bytes[BUFSIZ] = {'\0'}; long long values[4] = {64, 65, 66, 67}; pack(bytes, "iiii", values); for (int j = 0; j < 16; j++) { myVector.push_back(bytes[j]); } } time(&end); auto v2 = std::vector<unsigned char>(myVector.begin(), myVector.begin() + 16); debug_print2("result: ", v2); double time_taken = double(end - start); std::cout << "pack time: " << std::fixed << time_taken << std::setprecision(5); std::cout << " sec " << std::endl; return 0; }
5条答案
按热度按时间mspsb9vt1#
如果你正在寻找一个类似于Python的struct包的完整解决方案,你可以看看Google's Protocol Buffers Library,使用它可以解决很多问题(例如字节序、语言可移植性、跨版本兼容性)。
ve7v8dk22#
这是一个开始:
Append_bytes
会将任何整数类型追加到使用std::vector<uint8_t>
表示的字节缓冲区中。值以big endian字节顺序打包。如果需要更改此设置,请调整append_fixed_width
以不同顺序遍历值。这些函数构建了一个原始字节缓冲区,所以解码它的人有责任知道里面有什么。IIRC,这也是
struct.pack
所做的;换句话说,struct.unpack
的调用者需要提供相同的格式字符串,您可以编写append_fixed_width
的变体来打包TLV:我会认真考虑Jeremy的建议,我希望在我编写现在所有的二进制打包代码时它就已经存在了。
h9a6wy2h3#
您需要通过typedef定义一个精确宽度的整数类型;如果使用C99,则
int16_t
是在<stdint.h>
中预定义的。然后可以强制转换为该类型,并键入变量的内存表示:注意,您仍然需要处理字节序。
如果你没有C99,你可以使用编译时或运行时大小测试。对于编译时测试,考虑使用autoconf,它已经计算了各种基元类型的大小,这样你就可以在编译时选择一个好的类型。在运行时,只需进行一系列sizeof测试。注意,这在运行时有点不合适。因为测试总是得到相同的结果。作为autoconf的替代方法,您也可以使用编译器/系统标识宏进行编译时测试。
8qgya5xd4#
C++的方法是使用
stringstream
:要获得缓冲区,可以使用
ss.str().c_str()
。62o28rlo5#
我在c/c中进行了此实现,以比较python/php/dart/chttps://github.com/dart-lang/sdk/issues/50708之间的pack函数的执行时间