2009-02-11 4 views
19

Я люблю, как в питоне я могу сделать что-то вроде:C++ строка синтаксического анализа (питон стиль)

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

В основном это читает список строк, где каждый из них представляет собой точку в 3D пространстве, точка представляется как три числа, разделенные запятыми

Как это можно сделать на C++ без слишком большой головной боли?

Производительность не очень важна, этот синтаксический анализ происходит только один раз, поэтому простота важнее.

P.S. Я знаю, что это звучит как вопрос новичков, но поверьте мне, я написал лексер в D (в значительной степени похожий на C++), который включает в себя чтение некоторого текстового символа с помощью char и распознавание токенов,
Это просто, вернувшись на C++ после длительный период питона, просто заставляет меня не тратить время на такие вещи.

+18

Как о некоторых примерах из следующих, они несколько питон -esq: http: //www.cod eproject.com/KB/recipes/Tokenizer.aspx Кроме того, они очень эффективны и несколько элегантны. – 2010-11-04 02:04:33

ответ

24

I`d сделать что-то вроде этого:

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); 
} 

х, у, г должно быть поплавков.

И включают в себя:

#include <iostream> 
#include <fstream> 
+2

Если вы решили перейти от использования поплавков к использованию удвоений, не забудьте изменить каждый% f на% lf. Решение, использующее оператор >>() вместо sscanf(), в этом случае не нуждается в изменении. –

+0

Я принял этот ответ для краткости и прямолинейности :) – hasen

3

Вы можете прочитать файл из строки std :: iostream по строкам, поместить каждую строку в std :: string, а затем использовать boost :: tokenizer, чтобы разбить его. Это не будет столь же элегантным/коротким питона один, но намного проще, чем читать вещи в характере в то время ...

+0

boost: tokenizer действительно хорош :) –

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; 
} 
+1

неэффективен, но +1 для хорошего стиля – 2009-02-11 16:04:19

+0

@Iraimbilanja: Хотя я пересекаю строку дважды (сначала используя replace(), затем через iss), я подозреваю, что это по крайней мере так же быстро на практике как и другие решения, за исключением, возможно, подхода, основанного на kscanf(). Процессоры хороши при замене(). –

7

Этот ответ основан на предыдущем ответе на j_random_hacker и использует Духа Духа.

#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; 
} 
+1

Может ли какой-нибудь нисходящий помочь объяснить -1? –

+2

Может быть, потому, что использование boost :: spirit для разбора разделенных запятыми списков является излишним? Boost :: spirit существенно влияет на время компиляции. – JBeurer

+1

Возможно, из-за того, что вы создаете правило внутри цикла, обычно это будет огромным источником неэффективности, вам лучше было бы определить его вне цикла. - Дух излишний, добавляет огромное количество времени компиляции и почти невозможно отлаживать, предупреждения компилятора и сообщения об ошибках просто непонятны. – 2011-01-10 20:18:44

16

Все эти хорошие примеры в сторону, в 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.

+0

Это определенно способ сделать это, если вы будете вводить объекты Point в нескольких разных местах вашего кода. –

+1

Whoa. Downvoted? Для чего? –

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 

Это до вас, чтобы решить, что не это ошибка или особенность :)

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); 
} 
+0

Fugly, вы знаете. Вы читаете CSV, и вы делаете его похожим на ракетостроение. Будь проще. – JBeurer

17

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

1

Один из проектов с открытым исходным кодом Sony Picture Imagework является Pystring, который должен сделать для главным образом, прямой перевод частей, разделяющих струну:

Pystring - это набор функций C++, которые соответствуют интерфейсу и поведению методов класса строки python с помощью std :: string. Реализованный в C++, он не требует или не использует интерпретатор python. Это обеспечивает удобство и знакомство для обычных строковых операций, не включенных в стандартной библиотеке C++

Есть a few examples и some documentation

1

всех эти являются хорошими примерами. пока они не отвечают на следующий:

  1. файл CSV с различными номерами столбцов (некоторые строки с большим количеством столбцов, чем другие)
  2. или когда некоторые из значений имеют пустое пространство (я уЬ, x1 x2,, x2 ,)

так и для тех, кто еще ищет, этот класс: http://www.codeguru.com/cpp/tic/tic0226.shtml довольно прохладно ... некоторые изменения могут быть необходимы

Смежные вопросы