我需要读入如下所示的数据文件:
* 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()
将字符串转换为float。但我如何做最简单的休息呢?
我听说过字符串缓冲区,这对我有帮助吗?
答案 0 :(得分:18)
String Toolkit Library (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 中找到更多示例。
答案 1 :(得分: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
,BTW?通常,double
要好得多。
编辑,因为有人质疑是否返回vector
的副本是个好主意:
对于第一个解决方案,我当然会做到这一点。函数是将文件读入vector
,函数最明显的事情就是返回结果。这是否会导致明显的减速取决于很多事情(向量的大小,函数调用的频率以及从哪里读取的磁盘速度,编译器是否可以应用RVO)。我不想通过优化破坏显而易见的解决方案,但如果分析确实显示这是慢的,则应该按照非const引用传递向量。
(另请注意,带有rvalue支持的C ++ 1x,希望很快就可以通过你附近的编译器获得,这将使得这个讨论没有实际意义,因为它会阻止向量从函数返回时被复制。)
答案 2 :(得分: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)
只是一个示例,您需要在那里手动执行对该第一行的任何错误检查。
答案 3 :(得分: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
}