Синтаксический анализ строки С++ (стиль python)
Мне нравится, как в python я могу сделать что-то вроде:
points = []
for line in open("data.txt"):
a,b,c = map(float, line.split(','))
points += [(a,b,c)]
В основном это чтение списка строк, где каждый представляет точку в трехмерном пространстве, точка представляется в виде трех чисел, разделенных запятыми
Как это можно сделать на С++ без слишком большой головной боли?
Производительность не очень важна, этот синтаксический анализ происходит только один раз, поэтому простота важнее.
P.S. Я знаю, что это звучит как вопрос новичков, но поверьте мне, я написал лексер в D (очень похожий на С++), который включает в себя чтение некоторого текста char на char и распознавание токенов,
это просто, что, вернувшись на С++ после долгого периода python, просто заставляет меня не тратить время на такие вещи.
Ответы
Ответ 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 должны быть floats.
И включите:
#include <iostream>
#include <fstream>
Ответ 2
С++ 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;
}
Дополнительные примеры можно найти Здесь
Ответ 3
Все эти хорошие примеры, в С++, вы обычно переопределяете operator >>
для вашего типа точки, чтобы добиться чего-то вроде этого:
point p;
while (file >> p)
points.push_back(p);
или даже:
copy(
istream_iterator<point>(file),
istream_iterator<point>(),
back_inserter(points)
);
Соответствующая реализация оператора может очень похож на код j_random_hacker.
Ответ 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;
}
Ответ 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;
}
Ответ 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, чтобы разбить его. Это будет не так элегантно/коротко, как питон, но намного проще, чем читать вещи в персонаже за раз...
Ответ 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);
}
Ответ 9
Один из проектов с открытым исходным кодом Sony Picture Imagework Pystring, который должен сделать для главным образом прямой перевод расщепления строк части:
Pystring - это набор функций С++, которые соответствуют интерфейсу и поведению методов класса строки pythons с помощью std::string. Реализованный в С++, он не требует или не использует интерпретатор python. Это обеспечивает удобство и знакомство с обычными строковыми операциями, не включенными в стандартную библиотеку С++.
Есть несколько примеров и некоторая документация
Ответ 10
все это хорошие примеры. но они не отвечают на следующее:
- CSV файл с разными номерами столбцов (несколько строк с большим количеством столбцов, чем другие)
- или когда некоторые из значений имеют пробел (ya yb, x1 x2, x2,)
так для тех, кто все еще смотрит, этот класс:
http://www.codeguru.com/cpp/tic/tic0226.shtml
довольно круто... некоторые изменения могут потребоваться