【问题标题】:Initialization of istream_iterator causes ifstream.fail() to be setistream_iterator 的初始化导致设置 ifstream.fail()
【发布时间】:2013-01-26 22:01:54
【问题描述】:

我正在尝试将大量数据从文件导入到 boost::dynamic_bitset。为此,我希望使用与 dynamic_bitset (uint32_t) 的块大小相匹配的 istream_iterator。

如下所示,我使用要导入的文件的位置来设置我的 ifstream。但是,一旦我使用 ifstream 初始化 istream_iterator,就会设置 ifstream 的失败位。

关于为什么会发生这种情况的任何建议?

ifstream memHashes (hashFileLocation, ios::in | ios::binary);
if(memHashes.is_open() == false || memHashes.good() == false) { break; }
std::istream_iterator<uint32_t> memHashesIt(memHashes);
std::istream_iterator<uint32_t> memHashesEOFIt;

根据 cplusplus.com

failbit 通常由错误发生时的输入操作设置 和操作本身的内部逻辑有关,所以其他 流上的操作可能是可能的。虽然 badbit 通常是 当错误涉及流的完整性丢失时设置,这 即使对 溪流。可以通过调用成员函数独立检查badbit 不好。

编辑:

哈希包含 160 位哈希,由单独的 C 应用程序中的 SHA1 实现生成。该文件中有几千个哈希值。我想读取 5 个 4 字节的块,而不是 20 个 1 字节的块(因此我使用 uint32_t 作为块大小)我从 C 应用程序中提取了相关代码,它显示了正在生成的哈希然后写入文件:

#define HASH_SIZE 20 // 160 bits / 8 bits per byte = 20 bytes

FILE *fp;
fp = fopen(hash_filename, "wb");
if (!fp) {
    MSG("Hash dump file cannot be opened");
    fclose(fp);
    return NULL;
}

uint8_t *p;
unsigned char hash[HASH_SIZE];
SHA1((unsigned char*)p, LENGTH_TO_HASH, hash);
fwrite(hash, HASH_SIZE, 1, fp);

【问题讨论】:

  • 我认为所有 fstream 都是基于字符的
  • @Non-StopTimeTravel 将 uint32_t 更改为 uint8_t 会使错误消失。令人失望的是,读取 4 个字节的块可能比读取 1 个字节的块更有效。不确定我是否理解为什么这是不可能的——istream_iterator 不应该一次简单地从 ifstream 读取 4 个字节吗?
  • 代码应该可以工作,除非数据不能被读取为uint32_t,文件包含什么?
  • @JesseGood 我添加了有关文件制作者的详细信息。生产者正在将多个 chars / uint8_ts 写入文件。我希望将 4 个 uint8_t 块作为单个 uint32_t 块读取。
  • @Bschlinker:按照这种逻辑,vector::iterator 应该能够遍历 vectoe,这是没有意义的。迭代器匹配容器元素类型,因此通过相同的基本推理,它们也应该匹配流单元类型

标签: c++ file-io ifstream istream-iterator


【解决方案1】:

std::istream_iterator&lt;T&gt; 将输入 operator&gt;&gt;() 用于 T 类型的对象。也就是说,它假定格式化输入。在构造时,它会尝试读取第一个元素,这可能会导致 std::istream 设置为 std::ios_base::failbit

【讨论】:

    【解决方案2】:

    我认为初始化将从流中读取一个 uint32_t。类型 uint32_t 是 unsigned 或 unsigned long 的别名。我有一种令人毛骨悚然的感觉,即您的文件不包含数字,但您期望(参见例如 ios_base::binary 开放模式)流可以读取一些打包的非文本表示形式。如果是这种情况,那么您的期望就是错误的,但是如果不了解更多有关您的程序的信息,就很难判断。不过需要注意的是:如果您将 istream_iterator 读到最后,您将始终同时设置 eofbit 和 failbit。我猜你只设置了故障位,这表明解析错误。

    【讨论】:

    • 我添加了额外的信息来显示文件是如何生成的。是的——它是二进制流而不是 ASCII 流(我猜这就是你所说的packed 的意思?)为什么会阻止迭代器工作?
    • 迭代器只是在内部执行in &gt;&gt; var。失败的原因是它将提取文本,然后将其解析为数字。二进制标志不会改变这一点,请阅读上面的文档。您需要使用 read() 来检索单个字节。请注意,read 是一个“未格式化的输入函数”,这正是 Dietmar 在上面所暗示的。
    【解决方案3】:

    问题是你有二进制数据。

    istream_iteratoristreambuf_iterator 使用operator&gt;&gt; 读取数据。对于 uint_32_t 这意味着它将读取人类可读的文本并将其转换为整数。对于二进制数据,这将失败(大部分时间)。

    您对速度还有另一个误解。
    一次读取 4 个字节不太可能比一次读取 1 个字节快(这会使代码更复杂,这可能会减慢速度,但读取速度不会有差异)。这是因为从流中读取是缓冲的。当您进行读取时,一个巨大的块已经被读入缓冲区,它只是将它从一个位置复制到另一个位置。

    您真正想要做的是定义一个类并将数据作为一个单元复制到您的类中:

    class ShaMine
    {
        std::vector<char>  data;
        public:
            ShaMine(): data(20, '\0') {}
    
            friend std::istream& operator>>(std::istream& s, ShaMine& dst)
            {
                return s.read(&data[0], 20);
            }
    
            void poop(std::ostream& s)
            {
                 s << "Hi there: Char 0 is :" << (int) data[0] << "\n";
            } 
    };
    
    int main()
    {
         std::ifstream   sfile("FILE");
    
         for(std::istream_iterator<ShaMine> loop(sfile); loop != std::istream_iterator<ShaMine>(); ++lop)
         {
             loop->poop(std::cout);
         }
    };
    

    【讨论】:

    • en.cppreference.com/w/cpp/iterator/istream_iterator 意味着每次迭代器递增时都会读取数据,通过我对代码的分析,这似乎是正确的。 真正的读操作是在迭代器递增时进行的
    • 当然,只读意味着从底层对象读取,它可能缓冲了文件 I/O。话虽如此,在我的分析中,增加迭代器会带来显着的性能成本。
    • @BSchlinker:是的。但是工作代码比不工作的代码效率高 100%。先担心如何表达你的代码,然后再担心性能。此外,由于 C++11 和移动运算符,从迭代器中复制出来的成本是微不足道的(因为它将被移动),这是另一种表明您此时不应该担心微不足道的增强的方式。
    猜你喜欢
    • 2012-09-25
    • 2016-10-07
    • 2023-03-10
    • 1970-01-01
    • 2021-10-08
    • 1970-01-01
    • 2012-11-05
    • 1970-01-01
    • 2022-01-23
    相关资源
    最近更新 更多