2013-03-11 8 views
5

Si consideri il seguente codice di esempio:Come posso leggere un 0xFF in un file con 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; 
} 

E un file di input contenente quanto segue: "foo \ xffbar":

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

Ora, per il test utilizzando clang libC++ vs 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 

Come si può vedere la versione di libC++ pensa che 0xff sia la fine del flusso e smette di leggere. Quindi questo porta a un paio di domande.

1) Si tratta di un bug in libC++ che dovrei segnalare? Le mie ricerche su google per bug esistenti non hanno trovato nulla.

2) C'è un buon modo per ovviare a questo problema?

EDIT

Il seguente codice funziona:

#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; 
} 

mi porta a credere che si tratta di un problema di conversione binaria, ma questo non spiega perché libstdC++ funziona correttamente.

EDIT2

Utilizzando un file senza binario funziona bene troppo:

ifstream ifs ("testin"); 

Quindi non v'è sicuramente qualcosa di sospetto. Sembra che potrebbe essere un problema nell'implementazione di cin, non l'iteratore.

+2

provare a eseguire l'output come 'int (* iit)', potrebbe anche essere che cout si trova in uno stato non valido dopo l'emissione 0xff – PlasmaHH

+0

@PlasmaHH improbabile; sta emettendo 'i' con il' endl's circostante. – ecatmur

+0

@PlasmaHH: davvero, non impedirebbe che '3' venga trasmesso tramite' cout' sull'ultima riga? –

risposta

5

Sfortunatamente c'è ancora un errore in libC++ (in aggiunta a quello evidenziato da ecatmur). Ecco la soluzione:

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(); 
     } 
    } 

Otterrò questo verificato al più presto. Scusa per l'errore. Grazie per averlo portato alla mia attenzione

Correzione Correggere la revisione 176822 al trunk svn pubblico libcxx. La correzione richiede un dylib ricompilato anche se la correzione si trova in un'intestazione.

+0

Questa sembra la risposta giusta. Sto indagando su come posso ricostruire il mio dylib per verificarlo. – vishvananda

+0

Su OS X è possibile provare un nuovo libC++. Dylib all'interno di una shell eseguendo: export DYLD_LIBRARY_PATH = "". Questo è molto più sicuro rispetto alla sostituzione di /usr/lib/libc++.1.dylib. Vedi http://libcxx.llvm.org per maggiori informazioni. –

+0

Ho appena creato dalla corrente svn e ho provato questo: 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 Ho lo stesso problema. Non sono sicuro se c'è qualcosa di sbagliato nella mia build. Ho provato ad attaccare qualche eccezione casuale lanciata nelle vicinanze in __std_stream e non sembra essere stata ripresa. Suggerimenti? – vishvananda

1

L'iteratore si sta estraendo dallo stream.
Il flusso deve essere aperto con la modalità binary per impedire qualsiasi traduzione dei dati originali.

Successivamente, non utilizzare char. Il tipo char può essere firmato, non firmato o meno, a seconda del compilatore. Si consiglia di utilizzare uint8_t durante la lettura di ottetti binari.

provare qualcosa di simile:

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

Quindi la conversione binaria è sicuramente parte di esso. Nota la modifica sopra usando un ifstream binario. Sembra comunque che ci sia qualcosa di strano nell'implementazione di libC++. – vishvananda

+0

Non sembra essere una conversione binaria. Vedi EDIT2 sopra. – vishvananda

2

Penso che potrebbe aver trovato un bug che è già stato risolto. This commit (da @Howard Hinnant) contiene le seguenti modifiche:

@@ -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); 

Si noterà che la versione precedente memorizzato il valore di ritorno di getc in char, che è un no-no per la ragione precisa che confonde il valore char0xff con il valore intEOF (ovvero -1).

Il bug si applica solo a cin poiché i metodi interessati sono su __stdinbuf, che è il tipo di libC++ utilizzato per implementare solo cin; ifstream ad es. utilizza basic_filebuf<char>.

Controllare il file libcxx/include/__std_stream sul sistema per vedere se ha questo bug; se lo fa, applica la patch e dovrebbe risolverla.

+0

Penso che @HowardHinnant abbia la risposta corretta. Cercando di verificare – vishvananda

Problemi correlati