从 std::vector<unsigned char> 读取二进制数据的最简单方法?
Simplest way to read binary data from a std::vector<unsigned char>?
我有一大块const std::vector<unsigned char>
形式的二进制数据,并希望能够从中提取单个字段,例如整数为 4 个字节,布尔值为 1 个字节等。这需要尽可能既高效又简单。例如。它应该能够就地读取数据,而无需将其复制到字符串或数组中)。它应该能够像解析器一样一次读取一个字段,因为数据块没有固定的格式。我已经知道如何确定在每种情况下要读取的字段类型 - 问题是在执行此操作的std::vector
之上获得一个可用的界面。
找不到一种简单的方法来将这些数据转换为易于使用的形式,从而为我提供有用的读取功能。 std::basic_istringstream<unsigned char>
给了我一个读取界面,但似乎我需要先将数据复制到临时std::basic_string<unsigned char>
中,这对于更大的数据块来说并不理想。
也许在这种情况下,我可以使用 streambuf 来就地读取数据,但似乎我需要派生我自己的 streambuf 类来做到这一点。
我突然想到,我可能只在向量的数据()上使用 sscanf,这似乎比C++标准库替代方案更简洁、更高效。编辑:被提醒sscanf不会做我错误地认为它做的事情,我实际上不知道在C或C++中做到这一点的干净方法。但是我错过了什么吗,如果是这样,那又是什么?
向量operator[]
访问向量中的数据。向量的数据保证存储在单个连续数组中,[]
返回对该数组成员的引用。您可以直接使用该引用,也可以通过 memcpy 使用该引用。
std::vector<unsigned char> v;
...
byteField = v[12];
memcpy(&intField, &v[13], sizeof intField);
memcpy(charArray, &v[20], lengthOfCharArray);
编辑 1:如果你想要比这更"方便"的东西,你可以尝试:
template <class T>
ReadFromVector(T& t, std::size_t offset,
const std::vector<unsigned char>& v) {
memcpy(&t, &v[offset], sizeof(T));
}
用法将是:
std::vector<unsigned char> v;
...
char c;
int i;
uint64_t ull;
ReadFromVector(c, 17, v);
ReadFromVector(i, 99, v);
ReadFromVector(ull, 43, v);
编辑2:
struct Reader {
const std::vector<unsigned char>& v;
std::size_t offset;
Reader(const std::vector<unsigned char>& v) : v(v), offset() {}
template <class T>
Reader& operator>>(T&t) {
memcpy(&t, &v[offset], sizeof t);
offset += sizeof t;
return *this;
}
void operator+=(int i) { offset += i };
char *getStringPointer() { return &v[offset]; }
};
用法:
std::vector<unsigned char> v;
Reader r(v);
int i; uint64_t ull;
r >> i >> ull;
char *companyName = r.getStringPointer();
r += strlen(companyName);
如果你的向量存储二进制数据,你不能使用 sscanf 或类似的东西,它们处理文本。将字节转换为布尔值非常简单
bool b = my_vec[10];
对于提取以大端序存储的无符号 int(假设您的整数为 32 位):
unsigned int i = my_vec[10] << 24 | my_vec[11] << 16 | my_vec[12] << 8 | my_vec[13];
16 位无符号短短将类似:
unsigned short s = my_vec[10] << 8 | my_vec[11];¨
如果你能负担得起Qt依赖,QByteArray有一个名为constructor的fromRawData(),它将现有的数据缓冲区包装在QByteArray中而不复制数据。使用该字节数组,您可以馈送QTextStream
。
我不知道标准流库中有任何这样的功能(当然,除了实现您自己的streambuf
),但我希望被证明是错误的:)
您可以使用描述您尝试提取的数据的结构。 您可以将数据从向量移动到结构中,如下所示:
struct MyData {
int intVal;
bool boolVal;
char[15] stringVal;
} __attribute__((__packed__));
// assuming all extracted types are prefixed with a one byte indicator.
// Also assumes "vec" is your populated vector
int pos = 0;
while (pos < vec.size()-1) {
switch(vec[pos++]) {
case 0: { // handle int
int intValue;
memcpy(&vec[pos], &intValue, sizeof(int));
pos += sizeof(int);
// do something with handled value
break;
}
case 1: { // handle double
double doubleValue;
memcpy(&vec[pos], &doubleValue, sizeof(double));
pos += sizeof(double);
// do something with handled value
break;
}
case 2: { // handle MyData
struct MyData data;
memcpy(&vec[pos], &data, sizeof(struct MyData));
pos += sizeof(struct MyData);
// do something with handled value
break;
}
default: {
// ERROR: unknown type indicator
break;
}
}
}
使用 for 循环遍历向量,并使用按位运算符访问每个位组。例如,要访问向量中第一个 usigned 字符的前四位:
int myInt = vec[0] & 0xF0;
要从右边读取第五位,紧跟在我们刚刚读取的块之后:
bool myBool = vec[0] & 0x08;
三个最低有效(最低)位可以像这样访问:
int myInt2 = vec[0] & 0x07;
然后,您可以对向量中的每个元素重复此过程(使用 for 循环)。
- 如何从dicom文件中读取二进制数据
- 如何在Qt中从数据库中检索二进制数据?
- 如何使用位字段将数据从二进制文件复制到结构中?
- readsome() 适合在 Windows 上读取二进制数据吗?
- 如何使用 redis-plus-plus 存储二进制数据,就像我想存储结构一样?@for_stack?
- 在CRC-16 CCITT中将数据从二进制解码为文本,我应该输入一个码字,使用CRC生成器进行编码
- 将包含二进制数据的 QByteArray 传递到按值运行
- C++ 尝试优化每一帧将数据打印到二进制文件
- 如何在 c++ 中生成十六进制二进制数据的 sha256 哈希?
- 在处理网络、二进制数据和序列化时应使用流或容器
- 我能确定从文件中读取的 32 字节二进制数据等于 256 位吗?
- C++:如何通过 curl 调用使用 HTTP post 请求发送二进制数据(protobuf 数据)
- 使用二进制数据更新 PostgreSQL 表
- 在打印方法中,难以在二进制树中打印数据
- 使用二进制数据和无符号字符
- sd_journal_send发送二进制数据.如何使用日志检索数据?
- 从带有 std::ifstream::read() 的文件中读取 int 遍历 char * 二进制数据
- 将文本和二进制数据连接到一个文件中
- 二进制模式 + 格式化文本操作或文本模式 + 二进制数据操作 - 有意义吗?
- 如何仅使用 std::filebuf 将数据(二进制模式)写入文件