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

后端 未结 4 1650
梦如初夏
梦如初夏 2021-01-31 19:48

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         


        
相关标签:
4条回答
  • 2021-01-31 20:29

    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.)

    0 讨论(0)
  • 2021-01-31 20:32

    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.

    0 讨论(0)
  • 2021-01-31 20:44

    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.

    0 讨论(0)
  • 2021-01-31 20:46

    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
    }
    
    0 讨论(0)
提交回复
热议问题