【问题标题】:How can i read a 0xFF in a file with libc++ istream_iterator?如何使用 libc++ istream_iterator 读取文件中的 0xFF?
【发布时间】:2013-03-11 17:06:50
【问题描述】:

考虑以下示例代码:

#include <iostream>

using namespace std;

int main()
{
  istreambuf_iterator<char> eos;
  istreambuf_iterator<char> iit(cin.rdbuf());
  int i;
  for (i = 0; iit != eos; ++i, ++iit) {
    cout << *iit;
  }
  cout << endl << i << endl;
}

还有一个包含以下内容的输入文件:“foo\xffbar”:

$ hexdump testin
0000000 66 6f 6f ff 62 61 72
0000007

现在使用 clang libc++ 与 gnu libstdc++ 进行测试:

$ make test
clang++ -std=c++11 -stdlib=libc++ -Wall -stdlib=libc++ -o bug-libcc bug.cpp
clang++ -std=c++11 -stdlib=libc++ -Wall -stdlib=libstdc++ -o bug-libstd bug.cpp
./bug-libcc < testin
foo
3
./bug-libstd < testin
foo�bar
7

如您所见,libc++ 版本认为 0xff 是流的结尾并停止读取。所以这就引出了几个问题。

1) 这是我应该报告的 libc++ 中的错误吗?我对现有错误的 google 搜索结果一无所获。

2) 有没有解决此问题的好方法?

编辑

以下代码有效:

#include <iostream>
#include <fstream>

using namespace std;

int main()
{
  ifstream ifs ("testin", ios::binary);
  istreambuf_iterator<char> eos;
  istreambuf_iterator<char> iit(ifs.rdbuf());
  int i;
  for (i = 0; iit != eos; ++i, ++iit) {
    cout << *iit;
  }
  cout << endl << i << endl;
}

让我相信这是一个二进制转换问题,但这并不能解释为什么 libstdc++ 可以正常工作。

EDIT2

使用没有二进制文件的文件也可以正常工作:

ifstream ifs ("testin");

所以肯定有一些可疑的事情发生。看起来这可能是 cin 实现中的问题,而不是迭代器。

【问题讨论】:

  • 尝试输出为int(*iit),也可能是输出0xff后cout处于badstate
  • @PlasmaHH 不太可能;它输出i 和周围的endls。
  • @PlasmaHH:真的,这不会阻止3 通过最后一行的cout 输出吗?
  • @PlasmaHH a) 我试过这个,中间没有使用 cout。 b) 我不明白 cout 会如何导致 (iit == eos) 成为真的,这显然会发生。注意 i 的值。

标签: c++ libstdc++ libc++ istream-iterator


【解决方案1】:

不幸的是,libc++ 中仍然存在一个错误(除了 ecatmur 指出的那个)。这是修复:

Index: include/__std_stream
===================================================================
--- include/__std_stream    (revision 176092)
+++ include/__std_stream    (working copy)
@@ -150,7 +150,7 @@
     {
         for (int __i = __nread; __i > 0;)
         {
-            if (ungetc(__extbuf[--__i], __file_) == EOF)
+            if (ungetc(traits_type::to_int_type(__extbuf[--__i]), __file_) == EOF)
                 return traits_type::eof();
         }
     }

我会尽快检查。对不起这个错误。感谢您提请我注意。

修复已提交的修订版 176822 到 libcxx 公共 svn 主干。修复需要重新编译的 dylib,即使修复位于标头中。

【讨论】:

  • 这看起来是正确的答案。我正在研究如何重建我的 dylib 以检查它。
  • 在 OS X 上,您可以通过执行以下操作在 shell 中测试新的 libc++.dylib:export DYLD_LIBRARY_PATH=""。这比实际替换 /usr/lib/libc++.1.dylib 安全得多。请参阅libcxx.llvm.org 了解更多信息。
  • 我刚刚从当前的 svn 构建并尝试了这个:export DYLD_LIBRARY_PATH=/Users/vishvananda/libcxx/lib clang++ -std=c++11 -Wall -g -stdlib=libc++ -nostdinc++ -I/ Users/vishvananda/libcxx/include -L/Users/vishvananda/libcxx/lib -o bug-libcc bug.cpp 我遇到了同样的问题。不确定我的构建是否有问题。我尝试在 __std_stream 附近抛出一些随机异常,但它看起来并没有被拾取。有什么建议吗?
  • nvm,我明白了。运行构建文件时必须设置 DYLD_LIBRARY_PATH。
【解决方案2】:

我认为您可能发现了一个已经修复的错误。 This commit@Howard Hinnant)包含以下更改:

@@ -104,7 +104,7 @@
     int __nread = _VSTD::max(1, __encoding_);
     for (int __i = 0; __i < __nread; ++__i)
     {
-        char __c = getc(__file_);
+        int __c = getc(__file_);
         if (__c == EOF)
             return traits_type::eof();
         __extbuf[__i] = static_cast<char>(__c);
@@ -131,7 +131,7 @@
                 if (__nread == sizeof(__extbuf))
                     return traits_type::eof();
                 {
-                    char __c = getc(__file_);
+                    int __c = getc(__file_);
                     if (__c == EOF)
                         return traits_type::eof();
                     __extbuf[__nread] = static_cast<char>(__c);

您会注意到旧版本将 getc 的返回值存储到 char 中,这是一个禁忌,因为它将 char0xffint 混淆了值EOF(即-1)。

该错误仅适用于cin,因为受影响的方法位于__stdinbuf,这是libc++ 仅用于实现cin 的类型; ifstream 例如使用basic_filebuf&lt;char&gt;

检查你系统上的libcxx/include/__std_stream文件看是否有这个bug;如果是,请应用补丁,它应该可以修复它。

【讨论】:

  • 我认为@HowardHinnant 有正确的答案。正在尝试验证
【解决方案3】:

迭代器正在从流中提取。
需要使用binary 模式打开流,以防止对原始数据进行任何转换。

接下来,不要使用charchar 类型可以是有符号、无符号或无符号,这取决于编译器。我建议在读取二进制八位字节时使用uint8_t

试试这样的:

#include <cstdint>
using std::uint8_t;
istreambuf_iterator<uint8_t> eos;

【讨论】:

  • 所以二进制转换肯定是其中的一部分。请注意上面使用二进制 ifstream 进行的编辑。尽管如此,在 libc++ 实现中似乎仍然发生了一些奇怪的事情。
  • 好像不是二进制转换的。请参见上面的 EDIT2。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-17
  • 1970-01-01
  • 2019-09-10
相关资源
最近更新 更多