C++ 字符串解析(python 风格)
Posted
技术标签:
【中文标题】C++ 字符串解析(python 风格)【英文标题】:C++ string parsing (python style) 【发布时间】:2010-10-06 20:59:23 【问题描述】:我喜欢在 python 中我可以执行以下操作:
points = []
for line in open("data.txt"):
a,b,c = map(float, line.split(','))
points += [(a,b,c)]
基本上它是读取一个行列表,其中每一行代表 3D 空间中的一个点,该点表示为三个用逗号分隔的数字
如何在 C++ 中做到这一点而不会让人头疼?
性能不是很重要,这种解析只发生一次,所以简单更重要。
附:我知道这听起来像是一个新手问题,但相信我,我已经用 D 语言编写了一个词法分析器(很像 C++),它涉及逐个字符读取一些文本字符并识别标记, 只是,在长时间的python之后回到C++,只是让我不想在这些事情上浪费我的时间。
【问题讨论】:
下面的一些例子怎么样,它们有点像python-esq:codeproject.com/KB/recipes/Tokenizer.aspx 此外,它们非常高效且有点优雅。 【参考方案1】:我会做这样的事情:
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>
【讨论】:
如果您决定从使用浮点数更改为使用双精度数,请不要忘记将每个 %f 更改为 %lf。在这种情况下,不需要更改使用 operator>>() 而不是 sscanf() 的解决方案。 为了简洁明了,我接受了这个答案:)【参考方案2】:除了所有这些很好的例子,在 C++ 中,您通常会为您的点类型覆盖 operator >>
以实现如下效果:
point p;
while (file >> p)
points.push_back(p);
甚至:
copy(
istream_iterator<point>(file),
istream_iterator<point>(),
back_inserter(points)
);
算子的相关实现可能看起来很像 j_random_hacker 的代码。
【讨论】:
如果您要在代码中的多个不同位置输入 Point 对象,这绝对是一种方法。【参考方案3】:C++ String Toolkit Library (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;
更多例子可以在Here找到
【讨论】:
【参考方案4】:#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;
【讨论】:
@Iraimbilanja:虽然我遍历字符串两次(首先使用 replace(),然后通过 iss),但我怀疑这在实践中至少与其他解决方案一样快,除了 klew 的可能例外基于 sscanf() 的方法。 CPU 擅长 replace()。【参考方案5】:此答案基于 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;
【讨论】:
也许是因为使用 boost::spirit 来解析逗号分隔的列表是一种矫枉过正? Boost::spirit 显着影响编译时间。 可能是因为您在循环内实例化规则,通常这会导致效率低下,最好在循环外定义它。 - Spirit 太过分了,增加了大量的编译时间,并且几乎无法调试,编译器警告和错误消息简直难以理解。【参考方案6】: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
由您决定这是错误还是功能:)
【讨论】:
【参考方案7】:您可以从 std::iostream 中逐行读取文件,将每一行放入 std::string 中,然后使用 boost::tokenizer 将其拆分。它不会像 python 那样优雅/简短,但比一次读取一个字符中的内容要容易得多......
【讨论】:
【参考方案8】:它远没有那么简洁,当然我没有编译它。
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);
【讨论】:
丑陋,你知道的。你正在阅读 CSV,你让它看起来像是某种火箭科学。保持简单。【参考方案9】:Sony Picture Imagework 的开源项目之一是Pystring,它应该可以直接翻译字符串拆分部分:
Pystring 是 C++ 函数的集合,它使用 std::string 匹配 python 字符串类方法的接口和行为。在 C++ 中实现,它不需要或使用 python 解释器。它为标准 C++ 库中不包含的常见字符串操作提供了方便和熟悉
有a few examples和some documentation
【讨论】:
【参考方案10】:所有这些都是很好的例子。但他们没有回答以下问题:
-
具有不同列号的 CSV 文件(某些行的列比其他行多)
或者当某些值有空格时(ya yb,x1 x2,,x2,)
所以对于那些还在寻找的人来说,这个类: http://www.codeguru.com/cpp/tic/tic0226.shtml 很酷...可能需要一些更改
【讨论】:
以上是关于C++ 字符串解析(python 风格)的主要内容,如果未能解决你的问题,请参考以下文章