如何从 ASCII 文件中读取数字 (C++)
Posted
技术标签:
【中文标题】如何从 ASCII 文件中读取数字 (C++)【英文标题】:How to read numbers from an ASCII file (C++) 【发布时间】:2010-11-22 08:51:50 【问题描述】:我需要读取如下所示的数据文件:
* 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()
将字符串转换为浮点数。但是我该怎么做最简单的呢?
我听说过字符串缓冲区,这对我有帮助吗?
【问题讨论】:
【参考方案1】:由于这被标记为 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
的副本是否是个好主意:
对于第一个解决方案,我当然会做显而易见的事情。函数正在将文件读入vector
,函数要做的最明显的事情就是返回其结果。这是否会导致明显的减速取决于很多因素(向量的大小、函数的调用频率以及从何处调用、读取磁盘的速度、编译器是否可以应用 RVO)。我不想通过优化破坏明显的解决方案,但如果分析确实表明这很慢,则应该按非常量引用传入向量。
(另请注意,具有右值支持的 C++1x,希望很快可以通过您附近的编译器提供,这将使这个讨论变得毫无意义,因为它会阻止向量在从函数返回时被复制。)
【讨论】:
通用的“读取所有浮点数”循环是float number; while (is >> number) result.push_back(number);
@sth:确实,这更简洁,虽然我不喜欢“数字”变量“泄漏”出循环。
@andreash:嗯,我刚刚注意到您示例中的10.00
似乎是标题的一部分。对不起,我忽略了这一点。但你似乎已经能够从中抽象出来......:)
您可以使用 std::copy 避免循环: 'std::copy( std::istream_iteratorString 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中找到。
【讨论】:
很有趣,尽管您应该清楚这仅适用于 c++0x 编译器。 非常正确,但是 lambda 可以很容易地放入结构样式谓词中。我认为对于风格和未来的参考(让我们在 1-2 年后面对它,上面的代码等将成为常态),对如何完成事情有不同的看法是个好主意。 我喜欢这个。很好地使用新的 lambda,即使这不是答案。 旧编译器的 lambda 表达式应该如何?【参考方案3】:使用 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
【讨论】:
【参考方案4】:我会这样做:
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)
只是一个示例,您需要在此处手动对第一行进行任何错误检查。
【讨论】:
为什么投反对票?我已经对此进行了测试,它正确地将文件中的每个浮点数读取到向量中。以上是关于如何从 ASCII 文件中读取数字 (C++)的主要内容,如果未能解决你的问题,请参考以下文章