19

我喜欢在 python 中我可以执行以下操作:

points = []
for line in open("data.txt"):
    a,b,c = map(float, line.split(','))
    points += [(a,b,c)]

基本上它正在读取一个行列表,其中每行代表 3D 空间中的一个点,该点表示为三个用逗号分隔的数字

如何在 C++ 中做到这一点而不会让人头疼?

性能不是很重要,这种解析只发生一次,所以简单性更重要。

PS我知道这听起来像是一个新手问题,但相信我,我已经用 D 语言编写了一个词法分析器(非常像 C++),它涉及逐个字符地读取一些文本字符并识别标记,
就是这样,在很长一段时间后回到 C++ python的时期,只是让我不想在这些事情上浪费我的时间。

4

10 回答 10

24

我会做这样的事情:

ifstream f("data.txt");
string str;
while (getline(f, str)) {
    Point p;
    sscanf(str.c_str(), "%f, %f, %f\n", &p.x, &p.y, &p.z); 
    points.push_back(p);
}

x,y,z 必须是浮点数。

并包括:

#include <iostream>
#include <fstream>
于 2009-02-11T10:33:17.250 回答
17

除了所有这些很好的例子,在 C++ 中,您通常会覆盖operator >>for 您的点类型以实现如下效果:

point p;
while (file >> p)
    points.push_back(p);

甚至:

copy(
    istream_iterator<point>(file),
    istream_iterator<point>(),
    back_inserter(points)
);

运算符的相关实现可能看起来很像 j_random_hacker 的代码。

于 2009-02-11T11:45:35.893 回答
17

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

#include <string>
#include <deque>
#include "strtk.hpp"

struct point { double x,y,z; }

int main()
{
   std::deque<point> points;
   point p;
   strtk::for_each_line("data.txt",
                        [&points,&p](const std::string& str)
                        {
                           strtk::parse(str,",",p.x,p.y,p.z);
                           points.push_back(p);
                        });
   return 0;
}

更多示例可以在这里找到

于 2009-04-20T15:57:05.953 回答
14
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <algorithm>     // For replace()

using namespace std;

struct Point {
    double a, b, c;
};

int main(int argc, char **argv) {
    vector<Point> points;

    ifstream f("data.txt");

    string str;
    while (getline(f, str)) {
        replace(str.begin(), str.end(), ',', ' ');
        istringstream iss(str);
        Point p;
        iss >> p.a >> p.b >> p.c;
        points.push_back(p);
    }

    // Do something with points...

    return 0;
}
于 2009-02-11T09:59:21.730 回答
7

该答案基于 j_random_hacker 先前的答案,并利用了 Boost Spirit。

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <boost/spirit.hpp>

using namespace std;
using namespace boost;
using namespace boost::spirit;

struct Point {
    double a, b, c;
};

int main(int argc, char **argv) 
{
    vector<Point> points;

    ifstream f("data.txt");

    string str;
    Point p;
    rule<> point_p = 
           double_p[assign_a(p.a)] >> ',' 
        >> double_p[assign_a(p.b)] >> ',' 
        >> double_p[assign_a(p.c)] ; 

    while (getline(f, str)) 
    {
        parse( str, point_p, space_p );
        points.push_back(p);
    }

    // Do something with points...

    return 0;
}
于 2009-02-11T10:19:55.300 回答
4

Boost.Tuples 的乐趣:

#include <boost/tuple/tuple_io.hpp>
#include <vector>
#include <fstream>
#include <iostream>
#include <algorithm>

int main() {
    using namespace boost::tuples;
    typedef boost::tuple<float,float,float> PointT;

    std::ifstream f("input.txt");
    f >> set_open(' ') >> set_close(' ') >> set_delimiter(',');

    std::vector<PointT> v;

    std::copy(std::istream_iterator<PointT>(f), std::istream_iterator<PointT>(),
             std::back_inserter(v)
    );

    std::copy(v.begin(), v.end(), 
              std::ostream_iterator<PointT>(std::cout)
    );
    return 0;
}

请注意,这并不严格等同于您问题中的 Python 代码,因为元组不必位于单独的行上。例如,这个:

1,2,3 4,5,6

将给出与以下相同的输出:

1,2,3
4,5,6

由您决定这是错误还是功能:)

于 2009-02-11T16:01:43.297 回答
3

您可以逐行从 std::iostream 读取文件,将每一行放入 std::string 中,然后使用 boost::tokenizer 将其拆分。它不会像 python 那样优雅/简短,但比一次读取一个字符中的内容要容易得多......

于 2009-02-11T09:55:27.700 回答
1

它远没有那么简洁,当然我没有编译它。

float atof_s( std::string & s ) { return atoi( s.c_str() ); }
{ 
ifstream f("data.txt")
string str;
vector<vector<float>> data;
while( getline( f, str ) ) {
  vector<float> v;
  boost::algorithm::split_iterator<string::iterator> e;
  std::transform( 
     boost::algorithm::make_split_iterator( str, token_finder( is_any_of( "," ) ) ),
     e, v.begin(), atof_s );
  v.resize(3); // only grab the first 3
  data.push_back(v);
}
于 2009-02-11T20:58:00.290 回答
1

Sony Picture Imagework 的开源项目之一是Pystring,它应该可以直接翻译字符串分割部分:

Pystring 是 C++ 函数的集合,它们使用 std::string 匹配 python 字符串类方法的接口和行为。在 C++ 中实现,它不需要或使用 python 解释器。它为标准 C++ 库中不包含的常见字符串操作提供了方便和熟悉

几个例子一些文档

于 2009-10-25T14:19:26.773 回答
1

所有这些都是很好的例子。但他们没有回答以下问题:

  1. 具有不同列号的 CSV 文件(某些行的列比其他行多)
  2. 或者当某些值有空格时(ya yb,x1 x2,,x2,)

所以对于那些还在寻找的人来说,这个类: http: //www.codeguru.com/cpp/tic/tic0226.shtml 很酷......可能需要一些改变

于 2011-04-19T15:15:48.213 回答