微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

从文件中读取动态位集写入的数据无法读取正确的数据

如何解决从文件中读取动态位集写入的数据无法读取正确的数据

所以我有一个包含三个数字的向量。 65、66 和 67。我将这些数字从 int 转换为二进制并将它们附加到一个字符串中。字符串变为 100000110000101000011(分别为 65、66、67)。我正在通过 dynamic_bitset 库将此数据写入文件我有 BitOperations 类,它可以读取和写入文件。当我从文件中读取数据而不是给出上述位时,它给了我这些 001100010100001000001 位。

这是我的 BitOperations 类:

import tkinter
import keyboard

root = tkinter.Tk()

def callback():
    if root.state() == "normal":
        root.iconify()
    else:
        root.deiconify()

keyboard.add_hotkey('1',callback)
root.mainloop()

这是调用这些操作的代码

#include <iostream>
#include <boost/dynamic_bitset.hpp>
#include <fstream>
#include <streambuf>
#include "Utility.h"
using namespace std;
using namespace boost;

template <typename T>
class BitOperations {
private:
    T data;
    int size;
    dynamic_bitset<unsigned char> Bits;
    string fName;
    int bitSize;

public:
    BitOperations(dynamic_bitset<unsigned char> b){
        Bits = b;
        size = b.size();
    }

    BitOperations(dynamic_bitset<unsigned char> b,string fName){
        Bits = b;
        this->fName = fName;
        size = b.size();
    }

    BitOperations(T data,string fName,int bitSize){
        this->data = data;
        this->fName = fName;
        this->bitSize = bitSize;
    }

    BitOperations(int bitSize,string fName){
        this->bitSize = bitSize;
        this->fName = fName;
    }

    void writetoFile(){
        if (data != ""){
            vector<int> bitTemp = extractIntegersFromBin(data);
            for (int i = 0; i < bitTemp.size(); i++){
                Bits.push_back(bitTemp[i]);
            }
        }
        ofstream output(fName,ios::binary| ios::app);
        ostream_iterator<char> osit(output);
        to_block_range(Bits,osit);
        cout << "File Successfully modified" << endl;
    }

    dynamic_bitset<unsigned char> readFromFile(){
        ifstream input(fName);
        stringstream strStream;
        strStream << input.rdbuf();
        T str = strStream.str();

        dynamic_bitset<unsigned char> b;
        for (int i = 0; i < str.length(); i++){
            for (int j = 0; j < bitSize; ++j){
                bool isSet = str[i] & (1 << j);
                b.push_back(isSet);
            }
        }
        return b;
    }
};

我做错了什么导致不正确的行为?

编辑:这里是 extractIntegersFromBin(string s) 函数

#include <iostream>
// #include <string.h>
#include <boost/dynamic_bitset.hpp>
#include "Utility/BitOps.h"

int main(){
    vector<int> v;
    v.push_back(65);
    v.push_back(66);
    v.push_back(67);

    stringstream ss;
    string st;
    for (int i = 0; i < v.size(); i++){
        ss = toBinary(v[i]);
        st += ss.str().c_str();
        cout << i << " )" << st << endl;
    }
    // reverse(st.begin(),st.end());
    cout << "Original: " << st << endl;

    BitOperations<string> b(st,"bits2.bin",7);
    b.writetoFile();
    BitOperations<string>c(7,"bits2.bin");
    boost::dynamic_bitset<unsigned char> bits;
    bits = c.readFromFile();
    string s;
    
    // for (int i = 0; i < 16; i++){
        to_string(bits,s);
        // reverse(s.begin(),s.end());
    // }
    cout << "Decompressed: " << s << endl;
}

编辑 2:这是 toBinary 的代码

vector<int> extractIntegersFromBin(string s){

    char tmp;
    vector<int> nums;

    for (int i = 0; s[i]; i++ ){
        nums.push_back(s[i] - '0');
    }

    return nums;
}

解决方法

您面临两个不同的问题:

  1. boost 函数 to_block_range 将通过在末尾附加零将输出填充到内部块大小。在您的情况下,内部块大小为 sizeof(unsigned char)*8 == 8。因此,如果您在 writeToFile 中写入文件的位序列不是 8 的倍数,则会写入额外的 0 以构成 8 的倍数。因此,如果您使用 readFromFile 重新读取位序列,则必须找到某种方法再次删除填充位。

  2. 如何表示位序列 (reference) 没有标准方法。根据场景,从左到右或从右到左(或一些完全不同的顺序)表示位可能更方便。因此,当您使用不同的代码段打印相同的位序列并且希望这些代码段打印相同的结果时,您必须确保这些代码段就如何表示位序列达成一致。如果一段代码从左到右打印,另一段从右到左打印,你会得到不同的结果。

让我们单独讨论每个问题:

关于问题 1

我知道您想在 bitSize 的内部块大小之上使用 boost::dynamic_bitset 变量定义自己的块大小。例如,在您的 main 方法中,您构造 BitOperations<string> c(7,"bits2.bin");。我理解这意味着您希望存储在文件中的位序列的长度是 7 的倍数。

如果这种理解是正确的,您可以通过读取文件大小然后将其四舍五入到您的块大小的最接近倍数来删除 to_block_range 插入的填充位。不过您应该注意,您目前没有在 BitOperation 构造函数或 writeToFile 中强制执行此契约(即通过确保数据大小是 7 的倍数)。

在您的 readFromFile 方法中,首先注意内部循环错误地考虑了 blockSize。因此,如果 blockSize7,则错误地仅考虑每个块的前 7 位。而由 to_block_range 写入的块使用每个 8 字节块的完整 1 位,因为 boost::dynamic_bitset 对您的 7- 一无所知位块大小。所以这会让你错过一些细节。

以下是如何修复代码的一个示例:

    size_t bitCount = (str.length()*8) / bitSize * bitSize;
    size_t bitsPerByte = 8;

    for (int i = 0; i < bitCount; i++) {
      size_t index = (i / bitsPerByte);
      size_t offset = (i % bitsPerByte);

      bool isSet = (str[index] & ( 1 << offset));
      b.push_back(isSet);
    }

这个例子首先通过将文件大小四舍五入到块大小的最接近倍数来计算总共应该读取多少位。然后它遍历输入中的完整字节(即由 boost::dynamic_bitset 写入的内部块),直到读取了目标位数。剩余的填充位被丢弃。

另一种方法是使用 boost::from_block_range。这允许您摆脱一些样板代码(即将输入读入一些字符串缓冲区):

  dynamic_bitset<unsigned char> readFromFile() {
    ifstream input{fName};

    // Get file size
    input.seekg(0,ios_base::end);
    ssize_t fileSize{input.tellg()};

    // TODO Handle error: fileSize < 0

    // Reset to beginning of file
    input.clear();
    input.seekg(0);

    // Create bitset with desired size
    size_t bitsPerByte = 8;
    size_t bitCount = (fileSize * bitsPerByte) / bitSize * bitSize;
    dynamic_bitset<unsigned char> b{bitCount};

    // TODO Handle error: fileSize != b.num_blocks() * b.bits_per_block / bitsPerByte

    // Read file into bitset
    std::istream_iterator<char> iter{input};
    boost::from_block_range(iter,{},b);

    return b;
  }

关于问题 2

一旦您解决了问题 1,boost::dynamic_bitset 写入文件的 writeToFile 将与 readFromFile 读取的相同。如果您使用相同的方法打印两者,则输出将匹配。但是,如果您使用不同的打印方法,并且这些方法在打印位的顺序上不一致,则会得到不同的结果。

例如,在程序的输出中,您现在可以看到“Original:”输出与“Decompressed:”相同,只是顺序相反:

Original: 100000110000101000011
...
Decompressed: 110000101000011000001

同样,这并不意味着 readFromFile 工作不正确,只是您使用不同的方式打印位序列。

Original: 的输出是通过直接从左到右打印 0 中的 1/main 输入字符串获得的。在 writeToFile 中,此字符串然后以与 extractIntegersFromBin 相同的顺序分解,并将每一位传递给 push_backboost::dynamic_bitset 方法。 push_back 方法附加到位序列的末尾,这意味着它会将您传递的每个位解释为比前一个 (reference) 更重要:

作用:将bitset的大小增加1,并将新的最高有效位的值设置为value。

因此,您的输入字符串被解释为输入字符串中的第一位是最低有效位(即序列的“第一个”位),而输入字符串的最后一位是最高有效位(即序列的“最后”位)。

而您使用 to_string 构造“解压缩:”的输出。从该方法的文档中,我们可以看到位序列的最低有效位将是输出字符串(reference)的最后位:

效果:将 b 的一个表示复制到字符串 s 中。如果设置了相应的位,则字符串中的字符为“1”,否则为“0”。字符串中的字符位置 i 对应位位置 b.size() - 1 - i。

所以问题很简单,to_string(按设计)的打印顺序与您手动打印输入字符串的顺序相反。因此,要解决此问题,您必须反转其中之一,即通过以相反的顺序迭代字符串来打印输入字符串,或反转 to_string 的输出。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。