18

我需要读取如下所示的数据文件:

* 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

将这个文件读入浮点数组的最优雅的方法是什么?

我知道如何将每一行读入一个字符串,并且我知道如何使用atof(). 但是我该怎么做最简单的呢?

我听说过字符串缓冲区,这对我有帮助吗?

4

4 回答 4

18

字符串工具包库(Strtk)为您的问题提供了以下解决方案:

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

更多示例可以在C++ String Toolkit (StrTk) Tokenizer中找到。

于 2009-08-24T21:42:40.153 回答
11

由于这被标记为 C++,因此最明显的方法是使用流。在我的脑海中,这样的事情可能会做:

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

为什么float,顺便说一句?通常,double要好得多。

编辑,因为有人质疑返回副本是否vector是个好主意:

对于第一个解决方案,我当然会做显而易见的事情。该函数正在将文件读入 avector中,函数要做的最明显的事情就是返回其结果。这是否会导致明显的减速取决于很多因素(向量的大小、函数的调用频率以及从何处调用、读取磁盘的速度、编译器是否可以应用 RVO)。我不想通过优化破坏明显的解决方案,但如果分析确实表明这很慢,则应该按非常量引用传入向量。

(另请注意,具有右值支持的 C++1x,希望很快可以通过您附近的编译器提供,这将使这个讨论变得毫无意义,因为它会阻止向量在从函数返回时被复制。)

于 2009-08-24T18:08:49.413 回答
2

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

如果你真的需要使用一个数组,你必须首先分配它(如果你知道大小,动态或静态)然后你可以使用相同的复制算法

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

但即使在这种情况下,我也建议使用 std::vector ,如果您需要将数据传递到一个接受数组的函数中,您始终可以将其作为指向第一个元素的指针传递:

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
}
于 2009-08-24T22:40:38.600 回答
2

我会做这样的事情:

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

注意: header_is_correct(header)只是一个示例,您需要在此处手动对第一行进行任何错误检查。

于 2009-08-24T19:49:18.523 回答