2009-08-24 6 views
18

ho bisogno di leggere nel file di dati che assomigliano a questo:Come leggere i numeri da un file ASCII (C++)

* SZA: 10.00 
2.648 2.648 2.648 2.648 2.648 2.648 2.648 2.649 2.650 2.650 
2.652 2.653 2.652 2.653 2.654 2.654 2.654 2.654 2.654 2.654 
2.654 2.654 2.654 2.655 2.656 2.656 2.657 2.657 2.657 2.656 
2.656 2.655 2.655 2.653 2.653 2.653 2.654 2.658 2.669 2.669 
2.667 2.666 2.666 2.664 2.663 2.663 2.663 2.662 2.663 2.663 
2.663 2.663 2.663 2.663 2.662 2.660 2.656 2.657 2.657 2.657 
2.654 2.653 2.652 2.651 2.648 2.647 2.646 2.642 2.641 2.637 
2.636 2.636 2.634 2.635 2.635 2.635 2.635 2.634 2.633 2.633 
2.633 2.634 2.634 2.635 2.637 2.638 2.637 2.639 2.640 2.640 
2.639 2.640 2.640 2.639 2.639 2.638 2.640 2.640 2.638 2.639 
2.638 2.638 2.638 2.638 2.637 2.637 2.637 2.634 2.635 2.636 
2.637 2.639 2.641 2.641 2.643 2.643 2.643 2.642 2.643 2.642 
2.641 2.642 2.642 2.643 2.645 2.645 2.645 2.645 

quello che sarebbe il modo più elegante per leggere questo file in un array di carri?

So leggere ogni singola riga in una stringa e so come convertire la stringa in float utilizzando atof(). Ma come faccio a fare il resto il più facile?

Ho sentito parlare di buffer di stringhe, potrebbe aiutarmi?

risposta

11

Poiché questo è contrassegnato come C++, il modo più ovvio sarebbe utilizzare gli stream. Fuori della parte superiore della mia testa, qualcosa di simile potrebbe fare:

std::vector<float> readFile(std::istream& is) 
{ 
    char chdummy; 
    is >> std::ws >> chdummy >> std::ws; 
    if(!is || chdummy != '*') error(); 
    std::string strdummy; 
    std::getline(is,strdummy,':'); 
    if(!is || strdummy != "SZA") error(); 

    std::vector<float> result; 
    for(;;) 
    { 
    float number; 
    if(!is>>number) break; 
    result.push_back(number); 
    } 
    if(!is.eof()) error(); 

    return result; 
} 

Perché float, BTW? Solitamente, double è molto meglio.

Modifica, da quando è stato messo in dubbio che il ritorno di una copia del vector è una buona idea:

Per una prima soluzione, mi piacerebbe sicuramente fare l'ovvio. La funzione è che legge un file in un vector e la cosa più ovvia per una funzione da fare è restituire il risultato. Se questo si traduce in un notevole rallentamento dipende da un sacco di cose (la dimensione del vettore, la frequenza con cui viene chiamata la funzione e da dove, la velocità del disco da cui viene letto, se il compilatore può applicare RVO). Non vorrei rovinare l'ovvia soluzione con un'ottimizzazione, ma se il profiling mostra effettivamente che questo è lento, il vettore dovrebbe essere passato per riferimento non const.

(Si noti inoltre che C++ 1x con supporto rvalue, speriamo che presto sia disponibile per mezzo di un compilatore vicino a te, renderà questa discussione discutibile, in quanto impedirà che il vettore venga copiato al ritorno dalla funzione.)

+0

Il generico "read all floats" -loop sarebbe 'float number; while (è >> numero) result.push_back (numero); ' – sth

+0

Anche se il tuo è ovviamente equivalente. – sth

+0

@sth: In effetti, questo è più conciso, anche se non mi piace la "variabile numero" che perde "dal ciclo". – sbi

2

vorrei fare qualcosa di simile:

std::ifstream input("input.txt"); 
std::vector<float> floats; 
std::string header; 
std::getline(input, header); // read in the "* SZA: 10.00" line 
if(header_is_correct(header)) { 
    float value; 
    // while we could successfully read in a float from the file... 
    while(input >> value) { 
     // store it in the vector. 
     floats.push_back(value); 
    } 
} 

NOTA:header_is_correct(header) è solo un esempio, è necessario attuare un controllo degli errori per quella prima linea ci manualmente.

+0

perché il downvote? Ho provato questo e legge correttamente ogni float dal file in un vettore. –

18

Il String Toolkit Library (Strtk) ha la seguente soluzione al vostro problema:

#include <iostream> 
#include <string> 
#include <deque> 
#include <iterator> 

#include "strtk.hpp" 

int main() 
{ 
    std::deque<float> flist; 
    strtk::for_each_line("file.txt", 
         [&flist](const std::string& line) 
         { strtk::parse(line," ",flist); } 
         ); 
    std::copy(flist.begin(),flist.end(), 
       std::ostream_iterator<float>(std::cout,"\t")); 
    return 0; 
} 

Più esempi si possono trovare in C++ String Toolkit (StrTk) Tokenizer.

+0

interessante, anche se dovresti essere chiaro che questo è solo per i compilatori C++ 0x. –

+18

Verissimo ma il lambda può essere facilmente inserito in un predicato di stile struct. Ho pensato per lo stile e per i riferimenti futuri (lascia che sia affrontato da 1-2 anni, il codice di cui sopra e allo stesso modo sarà la norma) che sarebbe una buona idea avere una visione diversa su come le cose possono essere fatte. –

+12

Mi è piaciuto questo. Buon uso dei nuovi lambda, anche se questa non può essere la risposta. –

2

soluzione semplice algoritmi utilizzando STL:

#include <vector> 
#include <iostream> 
#include <string> 
#include <iterator> 

struct data 
{ 
    float first; // in case it is required, and assuming it is 
       // different from the rest 
    std::vector<float> values; 
}; 

data read_file(std::istream& in) 
{ 
    std::string tmp; 
    data d; 
    in >> tmp >> tmp >> d.first; 
    if (!in) throw std::runtime_error("Failed to parse line"); 

    std::copy(std::istream_iterator<float>(in), std::istream_iterator<float>(), 
     std::back_inserter<float>(d.values)); 

    return data; 
} 

Se davvero bisogno di usare una matrice, è prima necessario allocare (sia dinamico o statico, se si conosce la dimensione) e quindi è possibile utilizzare la stessa copia algoritmo

// parsing the first line would be equivalent 
float data[128]; // assuming 128 elements known at compile time 
std::copy(std::istream_iterator<float>(is), std::istream_iterator<float>(), 
     data); 

Ma mi consiglia di utilizzare std :: vector anche in questo caso, se avete bisogno per passare i dati in una funzione che prende un array si può sempre passare come un puntatore al primo elemento:

void f(float* data, int size); 
int main() 
{ 
    std::vector<float> v; // and populate 
    f(&v[0], v.size()); // memory is guaranteed to be contiguous 
}