How to read numbers from an ASCII file (C++)

情到浓时终转凉″ 提交于 2019-12-03 05:11:57

问题


I need to read in data files which look like this:

* 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

What would be the most elegant way to read this file into an array of floats?

I know how to read each single line into a string, and I know how to convert the string to float using atof(). But how do I do the rest the easiest?

I've heard about string buffers, might this help me?


回答1:


Since this is tagged as C++, the most obvious way would be using streams. Off the top of my head, something like this might do:

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

Why float, BTW? Usually, double is much better.

Edit, since it was questioned whether returning a copy of the vector is a good idea:

For a first solution, I'd certainly do the obvious. The function is reading a file into a vector, and the most obvious thing for a function to do is to return its result. Whether this results in a noticeable slowdown depends on a lot of things (the size of the vector, how often the function is called and from where, the speed of the disk this reads from, whether the compiler can apply RVO). I wouldn't want to spoil the obvious solution with an optimization, but if profiling indeed shows that this is to slow, the vector should be passed in per non-const reference.

(Also note that C++1x with rvalue support, hopefully soon to be available by means of a compiler near you, will render this discussion moot, as it will prevent the vector from being copied upon returning from the function.)




回答2:


The String Toolkit Library (Strtk) has the following solution to your 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;
}

More examples can be found in C++ String Toolkit (StrTk) Tokenizer.




回答3:


I would do something like this:

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

NOTE: header_is_correct(header) is just an example, you will need to implement any error checking for that first line manually there.




回答4:


Simple solution using STL algorithms:

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

If you really need to use an array, you must first allocate it (either dynamically or statically if you know the size) and then you can use the same copy algorithm

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

But I would recommend using std::vector even in this case, if you need to pass the data into a function that takes an array you can always pass it as a pointer to the first element:

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
}


来源:https://stackoverflow.com/questions/1323824/how-to-read-numbers-from-an-ascii-file-c

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!