2013-03-11 5 views
5

Рассмотрим следующий пример кода:Как я могу прочитать 0xFF в файле с libC++ istream_iterator?

#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 

Теперь для теста с использованием лязг LibC++ против гну 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) Есть ли хороший способ обойти эту проблему?

EDIT

Следующий код работает:

#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, но не для итератора.

+2

Попробуйте сделать вывод как 'int (* iit)', также может быть, что cout находится в плохом состоянии после вывода 0xff – PlasmaHH

+0

@PlasmaHH маловероятно; он выводит 'i' с окружающими' endl'. – ecatmur

+0

@PlasmaHH: действительно, не помешало бы '3' выводить bein через' cout' на последней строке? –

ответ

5

К сожалению, в 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 в общедоступный svn-файл libcxx. Для исправления требуется повторно скомпилированный dylib, хотя исправление находится в заголовке.

+0

Это похоже на правильный ответ. Я изучаю, как я могу восстановить свой dylib, чтобы проверить это. – vishvananda

+0

На OS X вы можете протестировать новый libC++. Dylib внутри оболочки, выполнив: export DYLD_LIBRARY_PATH = "<путь к недавно созданному libC++. Dylib>". Это намного безопаснее, чем замена /usr/lib/libc++.1.dylib. См. Http://libcxx.llvm.org для получения дополнительной информации. –

+0

Я только что построил из текущего svn и пробовал это: export DYLD_LIBRARY_PATH =/Пользователи/vishvananda/libcxx/lib clang ++ -std = C++ 11 -Wall -g -stdlib = libC++ -nostdinC++ -I/Пользователи/vishvananda/libcxx/include -L/Users/vishvananda/libcxx/lib -o bug-libcc bug.cpp У меня такая же проблема. Не уверен, что что-то не так с моей сборкой. Я пробовал придерживаться случайного исключения, бросая рядом в __std_stream, и похоже, что он не подбирается. Предложения? – vishvananda

1

Итератор извлекается из потока.
Поток должен быть открыт с помощью режима binary, чтобы предотвратить любые переводы исходных данных.

Далее, не используйте char. Тип char может быть подписан, без знака или нет, в зависимости от компилятора. Я рекомендую использовать uint8_t при чтении двоичных октетов.

попробовать что-то вроде этого:

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

Таким образом, двоичное преобразование, безусловно, является частью этого. Обратите внимание на изменение выше, используя двоичный ifstream. По-прежнему кажется, что в реализации libC++ есть что-то странное. – vishvananda

+0

Это не похоже на двоичное преобразование. См. EDIT2 выше. – vishvananda

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, что нет-нет для точной причины, что он путает значение char0xff с номером intEOF (то есть, -1).

Ошибка применяется только к cin, потому что затронутые методы находятся на __stdinbuf, который является типом libC++, используемым для реализации только cin; ifstream, например. использует basic_filebuf<char>.

Проверьте в своей системе файл libcxx/include/__std_stream, чтобы узнать, есть ли эта ошибка; если это так, примените патч, и он должен его исправить.

+0

Я думаю, что у @HowardHinnant есть правильный ответ. Попытка проверить – vishvananda