Write/Read 位域结构 to/from 文件
Write/Read bit fields structure to/from file
我正在尝试将位域结构写入文件然后读取它。
例如:
typedef struct{
ushort
a:4,
b:4,
c:4,
d:4;
} teststruct;
我试着这样写和读
QDataStream &operator <<(QDataStream &st, const teststruct &a)
{
st <<a.a << a.b << a.c << a.d;
return st;
}
QDataStream &operator >>(QDataStream &st, teststruct &a)
{
st >>a.a >> a.b >> a.c >> a.d;
return st;
}
teststruct str1, str2;
str1.a = 1;
str1.b = 0;
str1.c = 1;
str1.d = 0;
QFile f("testfile");
f.open(QFile::WriteOnly);
QDataStream st(&f);
st << str1;
f.close();
f.open(QFile::ReadOnly);
QDataStream st(&f);
st >> str2;
f.close();
但是在QDataStream::operator>>
我得到了一个错误
error: cannot bind bitfield 'a.teststruct::a' to 'quint16& {aka short
unsigned int&}'
我可以使用 >>
运算符做什么,或者是否有其他方法可以将数据读取到我的结构中?
在您的示例中,您应该注意到保存到文件中的数据可能不正确。因此,例如,具有以下结构:
struct BitStruct
{
uint8_t b1:4;
uint8_t b2:4;
};
运算符写成:
QDataStream &operator <<(QDataStream &st, const BitStruct &a)
{
st <<a.b1 << a.b2;
return st;
}
当您将示例数据 BitStruct bits{0x1, 0x2};
写入文件时,您将写入 2 个字节。文件的二进制内容将是 0x01 0x02
,这可能不是您想要实现的。
这是因为调用 st << a.b1
导致 b1
字段被转换为 QDataStream
处理的一种类型,在这种情况下很可能是 quint8
(您可以在 docs 中阅读更多内容)。
要修复此行为,您可以将 QDataStream::operator<<
实现修改为:
st.writeRawData(reinterpret_cast<const char*>(&a), sizeof(BitStruct));
另一方面,要将数据读取到这种结构,您应该在 QDataStream::operator>>
实现中进行类似的更新:
st.readRawData(reinterpret_cast<char*>(&a), sizeof(BitStruct));
这将允许按预期以紧凑的方式编写结构并相应地读取特定的位字段。
这样您就可以通过一种方法获得整个结构 written/read,并且您不必担心结构(附加字段)的进一步增长和更新两个运算符实现。
我假设你有你的位域结构的原因是它的大小是 ushort
(实际上是 uint16_t
),并且按值传递它很便宜而且它需要最少的space 可能。这是一个好的理由,所以让我们继续吧。
请注意,该结构的内存布局与其磁盘布局没有任何关系!磁盘布局取决于您如何使用 QDataStream
及其运算符。您显示的磁盘布局浪费了 space 的 75% - 每个值占用 16 位,但只需要 4:
(uint16_t a) (uint16_t b) (uint16_t c) (uint16_t d)
修复它的关键是使用中间值作为结构和数据流之间的接口。
因此:
QDataStream &operator <<(QDataStream &st, const teststruct &a)
{
uint8_t v0 = (a.d << 4) | a.c;
uint8_t v1 = (a.b << 4) | a.a;
st << v0 << v1;
return st;
}
QDataStream &operator >>(QDataStream &st, teststruct &a)
{
uint8_t v0, v1;
st >> v0 >> v1;
a.a = v1;
a.b = v1>>4;
a.c = v0;
a.d = v0>>4;
return st;
}
磁盘布局现在没有浪费space,如下(使用伪类型):
[(uint4_t d) (uint4_t c)] [(uint4_t b) (uint4_t a)]
我正在尝试将位域结构写入文件然后读取它。
例如:
typedef struct{
ushort
a:4,
b:4,
c:4,
d:4;
} teststruct;
我试着这样写和读
QDataStream &operator <<(QDataStream &st, const teststruct &a)
{
st <<a.a << a.b << a.c << a.d;
return st;
}
QDataStream &operator >>(QDataStream &st, teststruct &a)
{
st >>a.a >> a.b >> a.c >> a.d;
return st;
}
teststruct str1, str2;
str1.a = 1;
str1.b = 0;
str1.c = 1;
str1.d = 0;
QFile f("testfile");
f.open(QFile::WriteOnly);
QDataStream st(&f);
st << str1;
f.close();
f.open(QFile::ReadOnly);
QDataStream st(&f);
st >> str2;
f.close();
但是在QDataStream::operator>>
我得到了一个错误
error: cannot bind bitfield 'a.teststruct::a' to 'quint16& {aka short unsigned int&}'
我可以使用 >>
运算符做什么,或者是否有其他方法可以将数据读取到我的结构中?
在您的示例中,您应该注意到保存到文件中的数据可能不正确。因此,例如,具有以下结构:
struct BitStruct
{
uint8_t b1:4;
uint8_t b2:4;
};
运算符写成:
QDataStream &operator <<(QDataStream &st, const BitStruct &a)
{
st <<a.b1 << a.b2;
return st;
}
当您将示例数据 BitStruct bits{0x1, 0x2};
写入文件时,您将写入 2 个字节。文件的二进制内容将是 0x01 0x02
,这可能不是您想要实现的。
这是因为调用 st << a.b1
导致 b1
字段被转换为 QDataStream
处理的一种类型,在这种情况下很可能是 quint8
(您可以在 docs 中阅读更多内容)。
要修复此行为,您可以将 QDataStream::operator<<
实现修改为:
st.writeRawData(reinterpret_cast<const char*>(&a), sizeof(BitStruct));
另一方面,要将数据读取到这种结构,您应该在 QDataStream::operator>>
实现中进行类似的更新:
st.readRawData(reinterpret_cast<char*>(&a), sizeof(BitStruct));
这将允许按预期以紧凑的方式编写结构并相应地读取特定的位字段。
这样您就可以通过一种方法获得整个结构 written/read,并且您不必担心结构(附加字段)的进一步增长和更新两个运算符实现。
我假设你有你的位域结构的原因是它的大小是 ushort
(实际上是 uint16_t
),并且按值传递它很便宜而且它需要最少的space 可能。这是一个好的理由,所以让我们继续吧。
请注意,该结构的内存布局与其磁盘布局没有任何关系!磁盘布局取决于您如何使用 QDataStream
及其运算符。您显示的磁盘布局浪费了 space 的 75% - 每个值占用 16 位,但只需要 4:
(uint16_t a) (uint16_t b) (uint16_t c) (uint16_t d)
修复它的关键是使用中间值作为结构和数据流之间的接口。
因此:
QDataStream &operator <<(QDataStream &st, const teststruct &a)
{
uint8_t v0 = (a.d << 4) | a.c;
uint8_t v1 = (a.b << 4) | a.a;
st << v0 << v1;
return st;
}
QDataStream &operator >>(QDataStream &st, teststruct &a)
{
uint8_t v0, v1;
st >> v0 >> v1;
a.a = v1;
a.b = v1>>4;
a.c = v0;
a.d = v0>>4;
return st;
}
磁盘布局现在没有浪费space,如下(使用伪类型):
[(uint4_t d) (uint4_t c)] [(uint4_t b) (uint4_t a)]