18 Stimmen

Wie man Zahlen aus einer ASCII-Datei liest (C++)

Ich muss Datendateien einlesen, die wie folgt aussehen:

* 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

Was wäre der eleganteste Weg, um diese Datei in ein Array von Floats zu lesen?

Ich weiß, wie man jede einzelne Zeile in eine Zeichenkette einliest, und ich weiß, wie man die Zeichenkette mit atof() . Aber wie kann ich den Rest am einfachsten erledigen?

Ich habe von String-Puffern gehört, könnte mir das helfen?

18voto

El String-Toolkit-Bibliothek (Strtk) hat die folgende Lösung für Ihr Problem:

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

Weitere Beispiele finden Sie unter C++ String Toolkit (StrTk) Tokenizer .

11voto

sbi Punkte 211669

Da dies als C++ getaggt ist, wäre der naheliegendste Weg die Verwendung von Streams. Aus der Spitze von meinem Kopf, etwas wie dies tun könnte:

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

Warum float ÜBRIGENS? Normalerweise, double ist viel besser.

Editar denn es wurde in Frage gestellt, ob die Rücksendung einer Kopie der vector ist eine gute Idee:

Für eine erste Lösung würde ich auf jeden Fall das Offensichtliche tun. Die Funktion es Einlesen einer Datei in eine vector und die naheliegendste Aufgabe einer Funktion ist die Rückgabe des Ergebnisses. Ob dies zu einer spürbaren Verlangsamung führt, hängt von vielen Dingen ab (der Größe des Vektors, wie oft die Funktion aufgerufen wird und von wo, der Geschwindigkeit der Festplatte, von der sie liest, ob der Compiler RVO anwenden kann). Ich möchte die offensichtliche Lösung nicht mit einer Optimierung verderben, aber wenn das Profiling tatsächlich zeigt, dass dies zu langsam ist, sollte der Vektor per Non-Const-Referenz übergeben werden.

(Beachten Sie auch, dass C++1x mit rvalue-Unterstützung, die hoffentlich bald in einem Compiler in Ihrer Nähe verfügbar sein wird, diese Diskussion überflüssig machen wird, da es verhindern wird, dass der Vektor bei der Rückkehr aus der Funktion kopiert wird).

2voto

Evan Teran Punkte 83711

Ich würde etwa so vorgehen:

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) ist nur ein Beispiel, Sie müssen die Fehlerprüfung für diese erste Zeile manuell implementieren.

2voto

Einfache Lösung mit STL-Algorithmen:

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

Wenn Sie wirklich ein Array verwenden müssen, müssen Sie es zuerst zuweisen (entweder dynamisch oder statisch, wenn Sie die Größe kennen) und dann können Sie denselben Kopieralgorithmus verwenden

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

Aber ich würde empfehlen, std::vector auch in diesem Fall zu verwenden, wenn Sie die Daten in eine Funktion übergeben müssen, die ein Array nimmt, können Sie es immer als Zeiger auf das erste Element übergeben:

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
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X