解析二进制文件。 什么是现代方式?
我有一个我知道的布局的二进制文件。 例如让格式如下所示:
该文件应该看起来像(为了便于阅读,我添加了空格):
5 hello 3 0.0 0.1 0.2 -0.3 -0.4 -0.5
这里5 - 是2个字节:0x05 0x00。 “你好” - 5个字节等。
现在我想读这个文件。 目前我这样做:
char buffer[2]
unsigned short len{ *((unsigned short*)buffer) };
。 现在我有一个字符串的长度。 vector<char>
并从这个向量创建一个std::string
。 现在我有字符串ID。 char bufferFloat[4]
和cast *((float*)bufferFloat)
。 这有效,但对我来说它看起来很丑。 我可以直接读取unsigned short
或float
或string
等没有char [x]
创建? 如果不是,那么正确投射的方式是什么(我阅读过我使用的这种风格 - 是旧式风格)?
PS:在我写了一个问题的时候,我头脑中提出的更清晰的解释 - 如何从char [x]
任意位置投射任意数量的字节?
更新:我忘了明确提到字符串和浮点数据长度在编译时是未知的,并且是可变的。
在C ++中可以正常工作的C方法是声明一个结构体:
#pragma pack(1)
struct contents {
// data members;
};
注意
然后将读取缓冲区直接转换为结构体类型:
std::vector<char> buf(sizeof(contents));
file.read(buf.data(), buf.size());
contents *stuff = reinterpret_cast<contents *>(buf.data());
现在如果你的数据的大小是可变的,你可以分成几个块。 为了从缓冲区中读取一个二进制对象,阅读器功能非常方便:
template<typename T>
const char *read_object(const char *buffer, T& target) {
target = *reinterpret_cast<const T*>(buffer);
return buffer + sizeof(T);
}
主要的优点是这样的阅读器可以专门用于更高级的c ++对象:
template<typename CT>
const char *read_object(const char *buffer, std::vector<CT>& target) {
size_t size = target.size();
CT const *buf_start = reinterpret_cast<const CT*>(buffer);
std::copy(buf_start, buf_start + size, target.begin());
return buffer + size * sizeof(CT);
}
现在在您的主解析器中:
int n_floats;
iter = read_object(iter, n_floats);
std::vector<float> my_floats(n_floats);
iter = read_object(iter, my_floats);
注意:正如Tony D所观察到的,即使您可以通过#pragma
指令和手动填充(如果需要)来获得对齐方式,您仍然可能遇到与处理器对齐方式不兼容的情况,其形式为(最佳情况)性能问题或(最差情况)陷阱信号。 只有在您控制了文件格式的情况下,这种方法才有意思。
如果它不是为了学习的目的,并且如果你有自由选择二进制格式的话,你最好考虑使用类似protobuf的东西,它将为你处理序列化并允许与其他平台和语言进行互操作。
如果你不能使用第三方API,你可以看看QDataStream
的灵感
目前我这样做:
加载文件到ifstream
读这个流到字符缓冲区[2]
将其转换为unsigned short
: unsigned short len{ *((unsigned short*)buffer) };
。 现在我有一个字符串的长度。
最后的风险是SIGBUS
,性能和/或排序问题。 我建议读两个字符,然后你可以说(x[0] << 8) | x[1]
(x[0] << 8) | x[1]
,反之亦然,如果需要校正字节序,使用htons
。
vector<char>
并从这个vector
创建一个std::string
。 现在我有字符串ID。 不需要...直接读入字符串:
std::string s(the_size, ' ');
if (input_fstream.read(&s[0], s.size()) &&
input_stream.gcount() == s.size())
...use s...
read
下4个字节并将它们转换为unsigned int
。 现在我迈出了一大步。 while
不是文件read
结束float
s同样的方式 - 为每个float
创建一个char bufferFloat[4]
和cast *((float*)bufferFloat)
。 更好的直接在读取数据unsigned int
S和floats
,作为这样编译器将确保正确对齐。
这有效,但对我来说它看起来很丑。 我可以直接读取unsigned short
或float
或string
等没有char [x]
创建? 如果不是,那么正确投射的方式是什么(我阅读过我使用的这种风格 - 是旧式风格)?
struct Data
{
uint32_t x;
float y[6];
};
Data data;
if (input_stream.read((char*)&data, sizeof data) &&
input_stream.gcount() == sizeof data)
...use x and y...
请注意,上面的代码避免了将数据读入可能未对齐的字符数组中,其中由于对齐问题导致在可能未对齐的char
数组(包括std::string
)中reinterpret_cast
数据是不安全的。 同样,如果文件内容在字节顺序上存在差异,您可能需要使用htonl
一些后读转换。 如果存在未知数量的float
s,则需要计算并分配足够的存储空间,使其至少有4个字节的对齐方式,然后将Data*
目标......将索引值超过y
的声明数组大小因为访问地址处的内存内容是分配的一部分,并保存从流中读入的有效float
表示。 更简单 - 但有一个额外的读取速度可能会更慢 - 读取uint32_t
然后new float[n]
读取new float[n]
并进一步read
入....
实际上,这种类型的方法可以工作,并且很多低级和C代码完全可以做到这一点。 可能帮助您阅读文件的“清洁”高级库最终必须在内部执行类似的操作。
链接地址: http://www.djcxy.com/p/40309.html