2009-08-24 13 views
18

J'ai besoin de lire dans les fichiers de données qui se présentent comme suit:Comment lire les numéros à partir d'un fichier 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 

Quelle serait la façon la plus élégante de lire ce fichier dans un tableau de chars?

Je sais comment lire chaque ligne dans une chaîne, et je sais comment convertir la chaîne pour flotter en utilisant atof(). Mais comment puis-je faire le reste le plus facile?

J'ai entendu parler des tampons de chaîne, cela pourrait-il m'aider?

Répondre

11

Puisqu'il est marqué comme C++, la manière la plus évidente serait d'utiliser des flux. Du haut de ma tête, quelque chose comme ça pourrait faire:

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

Pourquoi float, BTW? Habituellement, double est beaucoup mieux.

Modifier, car il a été demandé si en retournant une copie du vector est une bonne idée:

Pour une première solution, je certainement pas l'évidence. La fonction est en lisant un fichier dans un vector, et la chose la plus évidente pour une fonction à faire est de retourner son résultat. Que cela entraîne un ralentissement perceptible dépend de beaucoup de choses (la taille du vecteur, la fréquence à laquelle la fonction est appelée et d'où, la vitesse du disque à partir de laquelle il lit, si le compilateur peut appliquer RVO). Je ne voudrais pas gâcher la solution évidente avec une optimisation, mais si le profilage montre en effet qu'il faut ralentir, le vecteur devrait être passé par référence non-const. (Notez également que C++ 1x avec le support de rvalue, espérons-le bientôt disponible au moyen d'un compilateur près de chez vous, rendra cette discussion inutile, car elle empêchera le vecteur d'être copié au retour de la fonction.)

+0

Le paramètre générique "read all floats" -loop serait "float number"; while (est >> number) result.push_back (nombre); ' – sth

+0

Bien que le vôtre soit bien sûr équivalent. – sth

+0

@sth: En effet, c'est plus laconique, bien que je n'aime pas la variable 'fuite' hors de la boucle. – sbi

2

je ferais quelque chose comme ceci:

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

REMARQUE:header_is_correct(header) est juste un exemple, vous devrez implémenter une vérification d'erreur pour cette première ligne manuellement là.

+0

pourquoi la downvote? J'ai testé cela et il lit correctement chaque flotteur du fichier dans un vecteur. –

18

Le String Toolkit Library (Strtk) a la solution suivante à votre problème:

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

Plus d'exemples peuvent être trouvés dans C++ String Toolkit (StrTk) Tokenizer.

+0

intéressant, bien que vous devriez être clair que c'est seulement pour les compilateurs C++ 0x. –

+18

Très vrai mais le lambda peut tout aussi bien être placé dans un prédicat de style struct. Je pensais pour le style et les futures références (faisons face à elle dans 1-2 ans, le code ci-dessus et semblable sera la norme) que ce serait une bonne idée d'avoir une vue différente sur la façon dont les choses peuvent être faites. –

+12

J'ai aimé ça. Bon usage des nouveaux lambdas, même si cela ne peut pas être la réponse. –

2

solution simple en utilisant des algorithmes 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; 
} 

Si vous avez vraiment besoin d'utiliser un tableau, vous devez d'abord allouer (soit dynamique ou statique si vous connaissez la taille) et vous pouvez utiliser la même copie algorithme

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

Mais je vous conseille d'utiliser std :: vecteur même dans ce cas, si vous avez besoin de transmettre les données dans une fonction qui prend un tableau, vous pouvez toujours passer comme un pointeur vers le premier élément:

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 
} 
Questions connexes