2010-11-16 15 views
5

Per questo programma ho utilizzato solo separatori di campo da file di dati nello script di shell. Ma sto cercando di usare la funzione di libreria standard ifstream() per leggere da un file di dati. L'unico problema è sto ottenendo i dati in questo modoC++ Funzione ifstream e separatori di campo

A: KT5: 14: scrivania direzionale:

Questo è per una tabella di hash, e ho bisogno di separare i valori nella linea per la struttura di dati come bene come il tipo di transazione. Ho cercato sul Web e non ho trovato molto sui separatori di campo e ciò che ho trovato è stato abbastanza confuso.

La domanda quindi è, c'è un modo per impostare un separatore di campo con la funzione ifstream o c'è un'altra funzione di I/O di libreria standard che dovrei usare?

Grazie.

risposta

4

getline offre la possibilità di specificare un delimitatore. È quindi possibile leggere l'input da un flusso come una sequenza di string separate da _Delim:

template<class CharType, class Traits, class Allocator> 
    basic_istream< CharType, Traits >& getline(
     basic_istream< CharType, Traits >& _Istr, 
     basic_string< CharType, Traits, Allocator >& _Str, 
     CharType _Delim 
    ); 

Se si tratta di dati uniformemente strutturato potrebbe essere utile definire una struttura per contenere e attuare operator>> caricare ogni istanza da lo stream, utilizzando la funzione sopra riportata interna al codice dell'operatore.

Se è necessario elaborare più righe (in tal modo newline è un separatore di record e: un separatore di campo), caricare ogni riga a turno in un stringstream utilizzando basic_istream::getline e quindi postelaborare la linea nei campi come mostrato.

+1

Wow ... e non in senso positivo. Penso che starò con C. – onemasse

+0

grazie Steve !!! – rajh2504

+0

@onemasse, erm: la dichiarazione in realtà assomiglia a: 'istream & getline (istream & is, string & str, char delim);', non così male ora è? :), per non parlare di ciò che 'std :: getline' ti dà sull'equivalente C! :) – Nim

5

@Steve Townsend ha già indicato una possibilità. Se preferisci usare operator>> invece di std::getline, puoi farlo anche tu. Uno istream considera sempre gli spazi come separatore. Ogni stream ha una locale associata e ogni locale include una faccetta ctype. Il facet ctype è ciò che lo istream utilizza per determinare quali caratteri di input sono spazi bianchi.

Nel tuo caso, apparentemente vuoi che lo stream tratti solo le nuove righe e i due punti come "spazi bianchi" (cioè separatori), mentre il carattere dello spazio effettivo viene semplicemente considerato come un carattere "normale", non un separatore.

Per fare questo, è possibile creare un aspetto ctype come questo:

struct field_reader: std::ctype<char> { 

    field_reader(): std::ctype<char>(get_table()) {} 

    static std::ctype_base::mask const* get_table() { 
     static std::vector<std::ctype_base::mask> 
      rc(table_size, std::ctype_base::mask()); 

     rc['\n'] = std::ctype_base::space; 
     rc[':'] = std::ctype_base::space; 
     return &rc[0]; 
    } 
}; 

da usare, questo, è necessario "infondere" il flusso con una localizzazione utilizzando questo aspetto:

int main() { 
    std::stringstream input("A:KT5:14:executive desk:"); 

    // have the stream use our ctype facet: 
    input.imbue(std::locale(std::locale(), new field_reader())); 

    // copy fields from the stream to standard output, one per line: 
    std::copy(std::istream_iterator<std::string>(input), 
       std::istream_iterator<std::string>(), 
       std::ostream_iterator<std::string>(std::cout, "\n")); 
    return 0; 
} 

Sono il primo ad ammettere, tuttavia, che questo ha alcune carenze. Prima di tutto, local e facet sono in genere piuttosto scarsamente documentati, quindi più è probabile che i programmatori C++ lo trovino abbastanza difficile da comprendere (specialmente quando tutto il vero lavoro avviene "sotto le copertine", per così dire).

Un'altra possibilità è utilizzare Boost Tokenizer. In tutta onestà, questo è un po 'altro lavoro da usare - richiederà che tu faccia qualcosa come leggere una stringa, quindi suddividerla separatamente.Allo stesso tempo, è ben documentato, ampiamente conosciuto, e si adatta abbastanza bene ai preconcetti della gente su come fare cose del genere, che molte persone probabilmente troveranno più facile seguire nonostante la complessità extra.

+1

Concordo sul fatto che Locales/facet siano poco conosciuti da molti sviluppatori C++. Ma questo perché poche persone in realtà si preoccupano di I18N o L10N il loro codice e quindi non si preoccupano mai di imparare questa roba. Ma questa è un'altra ragione per continuare a farlo apparire il più spesso possibile per dimostrare che, se usato correttamente, rende il codice principale molto più facile da scrivere e capire. Questo è un altro trucco che ho imparato con le faccette e intendo utilizzarlo al più presto. –