【问题标题】:Why does std::copy (from istream to ostream) raises an ios::failure exception?为什么 std::copy (从 istream 到 ostream) 会引发 ios::failure 异常?
【发布时间】:2011-05-05 04:54:52
【问题描述】:

以下代码应将数据从 wifstream 复制到 wcout。 内容复制完成后,程序抛出 ios::failure 异常。

#include <string>
#include <iostream>
#include <sstream>
#include <fstream>
#include <locale>
#include <iterator>
#include <algorithm>


int main(void)
{
    std::locale::global(std::locale(""));

    std::wifstream is;
    is.exceptions( std::ios::failbit | std::ios::badbit );
    is.open("test.ts", std::ios::binary);

    is >> std::noskipws;

    std::istream_iterator<wchar_t, wchar_t> in(is);
    std::istream_iterator<wchar_t, wchar_t> end;

    std::copy(in, end,
              std::ostream_iterator<wchar_t, wchar_t>(std::wcout));

    return 0;
} 

流应该只在出现任何问题时抛出异常(参见异常掩码),而不是在 EOF 上。

【问题讨论】:

  • 设置流异常似乎是个好主意,但它通常不会像您期望的那样工作。相反,只需在使用输入之前检查流状态,例如if (stream &gt;&gt; var) {/*only now use var*/}.

标签: c++ stl stream


【解决方案1】:

为避免跳过空白,请使用 std::istreambuf_iterator

std::copy(std::istreambuf_iterator<wchar_t, wchar_t>(is),
          std::istreambuf_iterator<wchar_t, wchar_t>(),
          std::ostream_iterator<wchar_t, wchar_t>(std::wcout));

例外:

本地可能正在使用失败的 codecvt facet。
尝试注释掉 locale 行,看看会发生什么。

您是否尝试过打印异常是什么?

try
{
    // do work
}
catch(std::exception const& e)
{
    std::cout << e.what() << "\n";
}

【讨论】:

  • 看起来不错,但 noskipws 不会导致异常。即使我取消注释,也有例外。
  • 有一个错字,istreambuf_iterator 的第二个参数是一个 trait 类。
  • 异常消息是:basic_ios::clear,即使我删除了全局语言环境,也会引发异常。
【解决方案2】:

因为您使用的是std::istream_iterator,所以尝试读取流末尾之后的字符会同时设置eofbitfailbit(并且只有在设置了一些错误位之后,迭代器才会变得等于结束迭代器)

剥离基本要素并恢复为 char 以使其更简单,程序相当于:

#include <iostream>
#include <fstream>
int main()
{
    std::ifstream is("test.txt", std::ios::binary);
    is.exceptions(std::ios::failbit); // failbit only because that's what you get
    is >> std::noskipws;
    if(is)
        for(char c; is >> c;) // will throw!
            std::cout << c;
}

【讨论】:

  • 不完全等同,但应该是:for (char c; is &gt;&gt; c;) cout &lt;&lt; c;
  • @Roger Pate 据我了解,std::copy 即使流已经处于失败状态(并且输入迭代器已经等于结束迭代器)也不会执行一次 operator&gt;&gt; 因此而(是)。
【解决方案3】:

根据 §27.6.1.2.3/10:

在构造一个哨兵对象后,从in中提取一个字符,如果有的话,并将其存储在c中。否则,函数调用in.setstate(failbit)

因此,当它到达文件末尾并且无法再提取字符时,它将设置失败位,您已设置该位以产生异常。使用std::copy 不会改变行为——istream_iterator 通过operator&gt;&gt; 读取。

您可以更轻松地复制文件:

std::wifstream is("test.ts", std::ios::binary);
std::wcout << is.rdbuf();

【讨论】:

  • 这对我来说没有任何意义。如果底层streambuf返回traits_type::eof(),则流应该只设置eof位,而不是fail位。
  • 如果它说如果设置了 eof 位,operator&gt;&gt; 将在不尝试转换的情况下返回,那么就会发生这种情况——但事实并非如此。即使设置了 eof 位,它仍然会尝试转换,但失败了,因此设置了失败位。
  • @cytrinox: eofbit 被设置,然后 op>> 导致 failbit 被设置。
猜你喜欢
  • 1970-01-01
  • 2013-09-12
  • 2019-02-23
  • 2011-02-22
  • 1970-01-01
  • 2019-03-24
  • 2012-03-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多