在文件(C++)中查找n个位数

Seek n number of bits into a file (C++)

本文关键字:查找 文件 C++      更新时间:2023-10-16

我有一个文件,其中包含未按字节对齐的数据,我想在文件中查找(例如3位),然后开始将字符读取到缓冲区中。有没有一种"简单"的方法可以做到这一点?

如果可能的话,我希望避免对缓冲区中的每个字符进行移位。

除非您使用的是一个非常有趣的平台,否则您的文件包含字节。你一次读一个字节。因此,如果不进行比特转换,就没有办法做到这一点。隐藏我能想到的比特移位的最简单方法是制作一个输入迭代器,存储以前读取的字节,并在"后台"进行移位。

这里有一个非常简单的bit_iterator,它封装了一个仅向前的istream迭代器。它允许将单个比特提取到一个单词中。

这应该足以让你朝着正确的方向前进。

#include <iostream>
#include <iterator>
#include <iomanip>
#include <sstream>
// forward-only iterator for simplicity
template<class Iter>
struct bit_iterator
{
    using word_type = typename Iter::value_type;
    static constexpr int end_bit = sizeof(word_type) * 8;
    bit_iterator(Iter underlying) : _iter(underlying) {};
    word_type operator*() {
        if (_bitnum == end_bit)
        {
            _word = *_iter++;
            _bitnum = 0;
        }
        auto ret = (_word & (1 << _bitnum++)) ? 1 : 0;
        return ret;
    }
    bit_iterator& operator++() {
        return *this;
    }
    bit_iterator& operator++(int) {
        return *this;
    }
    Iter underlying() const {
        return _iter;
    }
    Iter _iter;
    word_type _word;
    int _bitnum = end_bit;
};
template<class Iter>
auto make_bit_iterator(Iter iter)
{
    return bit_iterator<Iter>(iter);
}
template<class Word, class Iter>
Word get_bits(Iter& iter, size_t bits = sizeof(Word) * 8)
{
    Word acc = 0;
    for ( ; bits ; --bits )
    {
        acc = (acc << 1) | *iter++;
    }
    return acc;
}
int main(int argc, char * argv[])
{
    std::istringstream is("abcdefghijklmnop");
    auto i = make_bit_iterator(std::istream_iterator<char>(is));
    auto first_three = get_bits<char>(i, 3);
    std::cout << std::hex << int(get_bits<std::uint8_t>(i)) << std::endl;
    std::cout << std::hex << get_bits<std::int32_t>(i) << std::endl;
    std::cout << std::hex << get_bits<std::uint16_t>(i) << std::endl;
    return 0;
}

预期结果:

32
36313533
3730

std::ifstream专门处理charT到char,这是标准提供的最小数据块。据我所知,在位级别上访问文件没有标准的低级别方式。您必须至少读取文件的一部分,并在代码中进行分析。

你可以在谷歌上找到一些std::stream适配器,它可以一点一点地阅读,但我不确定你是否真的需要这个。我在这里找到了一个:http://stanford.edu/~stepp/cppdoc/iptstream-class.html

[编辑]

以下是使用带位字段的结构读取此类文件的示例:

实时

#include <iostream>
#include <fstream>
#include <cassert>
#include <vector>
int main()
{
    // Write test data
    std::vector<bool> bit;
    bit.push_back(0);
    bit.push_back(0);
    bit.push_back(0);
    // Write test byte data, it is padded by 3 bits in front
    for (int n = 0; n < 80 * 13; ++n)
        bit.push_back((n / 8 % 2)==0);
    std::ofstream ofs("test.data", std::ifstream::binary);
    for (auto it = bit.begin(); it != bit.end(); ) {
        char toWrite = 0;
        for (int n = 7; it!=bit.end() && n >= 0; --n, ++it) toWrite |= *it << n;
        ofs.write(&toWrite, 1);
    }
    ofs.close();
    // Read data
    union S {
        struct {
            unsigned short pad : 5;
            unsigned short dat : 8;
            unsigned short oth : 3;
        } inner;
        char bytes[2];
    } data;
    // Create bit vector to verify if reading is correct.
    std::vector<bool> bit2;
    bit2.push_back(0); // First three bits are padding
    bit2.push_back(0);
    bit2.push_back(0);
    std::ifstream ifs("test.data", std::ifstream::binary);
    while(ifs.read(data.bytes, sizeof(data.bytes))) {
        std::swap(data.bytes[0], data.bytes[1]);
        // std::cout << data.inner.dat << ", ";
        for (int n = 7; n >= 0; --n) bit2.push_back(data.inner.dat & (1 << n));
        ifs.seekg(-1, ifs.cur);
    }
    assert(bit == bit2);
}