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)]