使用 istream_iterator 从文件中读取位集

Read bitset from file using istream_iterator

我正在尝试将文本文件读入 std::bitset 对象的向量中。该文件由 1 和空格(用于指示 0 或 false)组成,我试图通过重载输入运算符 >> 并使用 istream_iterators 来做到这一点。我首先将字符串中的空格更改为 0,然后构造一个位,然后将其推送到包含该字符串的向量。但是,当我打印输出时,输出看起来很奇怪,似乎我读取的元素多于文件中的行(位集的向量长于行数)。代码如下所示:

  
  template<long unsigned int N>
  std::istream& operator>>(std::istream& is, std::bitset<N>& b){
    std::string line;
    std::getline(is,line);
    for(unsigned long int i =0;i<N;++i){line[i]==' '? line[i]='0':'1';};
    std::cout << line << std::endl;
    b = std::bitset<N>(line);
    return is;
  }
 
 int main(){
  const unsigned long int N = 40;
  std::ifstream file("activities.txt");
  std::istream_iterator<std::bitset<N>> begin(file),end;
  std::vector<std::bitset<N>> bits;
  
  for(auto it = begin;it!=end;++it){
    bits.push_back(*it);
  }
  
  for(auto bit: bits){ std::cout << bit << std::endl;}
 }

我在不使用 istream_iterator 和重载运算符>>的情况下成功地做了同样的事情,但我只是想弄清楚为什么这种方法不起作用。

有趣的问题。

发生这种情况的原因是 std::bitset 已经有一个被覆盖的提取运算符 >>。请参阅 here。你可以读到它的行为就像一个格式化的输入函数。所以,它会一直读到下一个白色 space 然后停止。如果我们有一个“文件结尾”或直到它达到定义的 std::bitset.

的长度,它将结束读取

不小心你的源文件中有 spaces,它将作为这个格式化输入函数的分隔符,不会导致任何问题。

实际上,您应该在 std::vector 中看到很多“位集”,所有这些都由许多开头的前导“0”和结尾的几个不同长度的“1”组成。

这是读了很多'1'岛,然后用'0'填充左边的字符串的结果,这样最后的bitset有40个字符。

如果你想让C++调用你的函数,你需要打开std-namespace。虽然可以做到,但强烈建议不要这样做,模板特化除外。参见:

不推荐:

#include <iostream>
#include <fstream>
#include <string>
#include <iterator>
#include <bitset>
#include <vector>

constexpr unsigned long int N = 40;
namespace std {
    template<long unsigned int N>
    std::istream& operator>>(std::istream& is, std::bitset<N>& b) {
        std::string line;
        std::getline(is, line);
        for (unsigned long int i = 0; i < N; ++i) { line[i] == ' ' ? line[i] = '0' : '1'; };
        std::cout << line << std::endl;
        b = std::bitset<N>(line);
        return is;
    }
}

int main() {

    std::ifstream file("activities.txt");
    std::istream_iterator<std::bitset<N>> begin(file), end;
    std::vector<std::bitset<N>> bits;

    for (auto it = begin; it != end; ++it) {
        bits.push_back(*it);
    }

    for (auto& bit : bits) { std::cout << bit << std::endl; }
}

这基本上可以工作。但是,不应使用。此外,在我看来,>>-运算符的 for 循环中存在语义错误,通过与“N”而不是“line.length()”进行比较,后者会更好。因为,如果一行超过 40 个字符,space 将不会被转换,停留在 ' ' (space) 并导致无效输入异常。

更正(在任何情况下,一行中较长的字符串将被截断)并在“std”名称中使用允许的模板特化space,你可以写:

#include <iostream>
#include <fstream>
#include <string>
#include <iterator>
#include <bitset>
#include <vector>

constexpr unsigned long int N = 40;
namespace std {

    template<>
    std::istream& operator>>(std::istream& is, std::bitset<N>& b) {
        std::string line;
        std::getline(is, line);
        for (unsigned long int i = 0; i < line.length(); ++i) { line[i] == ' ' ? line[i] = '0' : '1'; };
        //std::cout << line << std::endl;
        b = std::bitset<N>(line);
        return is;
    }
}

int main() {

    std::ifstream file("activities.txt");
    std::istream_iterator<std::bitset<N>> begin(file), end;
    std::vector<std::bitset<N>> bits;

    for (auto it = begin; it != end; ++it) {
        bits.push_back(*it);
    }

    for (auto& bit : bits) { std::cout << bit << std::endl; }
}

这行得通。


利用 std::bitsets constructor number 3 可以将 spaces 解释为 0es,您可以让生活变得更简单。

并且,请使用 std::vectors range constructor (number 5).

这样,代码就更简单了:

#include <iostream>
#include <fstream>
#include <string>
#include <iterator>
#include <bitset>
#include <vector>

constexpr unsigned long int N = 40;
namespace std {

    template<>
    std::istream& operator>>(std::istream& is, std::bitset<N>& b) {
        std::string line;
        std::getline(is, line);
        b = std::bitset<N>(line, 0, N, ' ', '1');
        return is;
    }
}

int main() {

    std::ifstream file("activities.txt");

    std::vector<std::bitset<N>> bits(std::istream_iterator<std::bitset<N>>(file), {});

    for (auto& bit : bits) { std::cout << bit << std::endl; }
}