2009-02-11 36 views
19

Tôi yêu như thế nào trong python tôi có thể làm một cái gì đó như:C++ chuỗi phân tích cú pháp (python style)

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

Về cơ bản nó đang đọc một danh sách các dòng mà mỗi người đại diện cho một điểm trong không gian 3D, điểm được biểu diễn dưới dạng ba số được phân tách bằng dấu phẩy

Làm cách nào để thực hiện điều này trong C++ mà không bị đau đầu quá nhiều?

Hiệu suất không phải là rất quan trọng, việc phân tích cú pháp này chỉ xảy ra một lần, vì vậy sự đơn giản là quan trọng hơn.

P.S. Tôi biết nó có vẻ giống như một câu hỏi mới, nhưng tôi tin rằng tôi đã viết lexer trong D (khá giống với C++) liên quan đến việc đọc một số văn bản char bằng char và nhận ra các thẻ,
nó chỉ là, quay lại C++ sau thời gian dài của python, chỉ làm cho tôi không muốn lãng phí thời gian của tôi vào những thứ như vậy.

+18

Làm thế nào về một số ví dụ từ những điều sau đây, họ có phần python -esq: http: //www.cod eproject.com/KB/recipes/Tokenizer.aspx Hơn nữa, chúng rất hiệu quả và phần nào thanh lịch. –

Trả lời

24

I`d làm điều gì đó như thế này:

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 phải nổi.

Và bao gồm:

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

Nếu bạn quyết định thay đổi từ việc sử dụng phao nổi sang sử dụng đôi, đừng quên thay đổi mỗi% f thành% lf. Một giải pháp sử dụng toán tử >>() thay vì sscanf() không cần phải thay đổi trong trường hợp này. –

+0

Tôi chấp nhận câu trả lời này cho ngắn gọn và thẳng thắn :) – hasen

3

Bạn có thể đọc tệp từ std :: iostream line by line, đặt mỗi dòng vào một chuỗi std :: và sau đó sử dụng boost :: tokenizer để tách nó. Nó sẽ không được khá như tao nhã/ngắn như python chỉ một mà dễ dàng hơn nhiều so với đọc mọi thứ trong một nhân vật tại một thời điểm ...

+0

boost :: tokenizer thực sự tốt :) –

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

không hiệu quả nhưng +1 cho phong cách tốt –

+0

@Iraimbilanja: Mặc dù tôi đi qua chuỗi hai lần (lần đầu tiên sử dụng replace(), sau đó qua iss), tôi nghi ngờ điều này ít nhất là nhanh trong thực tế như các giải pháp khác, với ngoại lệ có thể của phương pháp dựa trên sscanf() của klew. CPU thay thế tốt(). –

7

Câu trả lời này được dựa trên câu trả lời trước bởi j_random_hacker và tận dụng của Tăng Thần.

#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

Có bất kỳ dịch vụ chăm sóc downvoter nào để giải thích -1? –

+2

Có thể vì sử dụng boost :: tinh thần để phân tách các danh sách được phân tách bằng dấu phẩy là một mức quá mức? Boost :: tinh thần ảnh hưởng đáng kể đến thời gian biên dịch. – JBeurer

+1

Có thể vì thực tế là bạn đang khởi tạo quy tắc bên trong vòng lặp, thông thường đây sẽ là một nguồn không hiệu quả rất lớn, bạn nên cải thiện nó bên ngoài vòng lặp. - Tinh thần là quá mức cần thiết, thêm số lượng lớn thời gian biên dịch và gần như không thể gỡ lỗi, cảnh báo trình biên dịch và thông báo lỗi đơn giản là không thể hiểu được. –

16

Tất cả những ví dụ tốt sang một bên, trong C++ bạn sẽ thường ghi đè operator >> cho loại quan điểm của bạn để đạt được một cái gì đó như thế này:

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

hoặc thậm chí:

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

Việc triển khai có liên quan của nhà điều hành có thể trông rất nhiều giống như mã bởi j_random_hacker.

+0

Đây chắc chắn là cách để làm điều đó nếu bạn sẽ nhập các đối tượng Điểm vào một số vị trí khác nhau trong mã của bạn. –

+1

Whoa. Downvoted? Những gì các f ... cho? –

4

Fun với 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; 
} 

Lưu ý rằng điều này không đúng tương đương với mã Python trong câu hỏi của bạn bởi vì các bộ không cần phải được trên dòng riêng biệt. Ví dụ, điều này:

1,2,3 4,5,6 

sẽ cho sản lượng tương tự hơn:

1,2,3 
4,5,6 

Đó là tùy thuộc vào bạn để quyết định xem đó là một lỗi hoặc một tính năng :)

1

nó hư không gần như ngắn gọn và dĩ nhiên tôi không biên dịch điều này.

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

Thú vị, bạn biết đấy. Bạn đang đọc CSV và bạn làm cho nó trông giống như một số loại khoa học tên lửa. Giữ nó đơn giản. – JBeurer

17

Các C++ String Toolkit Library (StrTk) có giải pháp sau để vấn đề của bạn:

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

Thêm ví dụ có thể được tìm thấy Here

1

Một trong những dự án mã nguồn mở Sony Picture Imagework là Pystring, mà nên làm cho bản dịch trực tiếp chủ yếu của các phần tách chuỗi:

Pystring là tập hợp các hàm C++ phù hợp với giao diện và hành vi của các phương thức lớp chuỗi của python sử dụng chuỗi std ::. Thực hiện trong C + +, nó không yêu cầu hoặc sử dụng một trình thông dịch python. Nó cung cấp sự tiện lợi và quen thuộc cho các hoạt động chuỗi thông thường không có trong chuẩn C++ thư viện

a few examples, và some documentation

1

tất cả những là những ví dụ tốt. nhưng họ không trả lời như sau:

  1. một file

    CSV với số cột khác nhau (một số hàng với các cột hơn những người khác)
  2. hoặc khi một số giá trị có khoảng trắng (ya yb, x1 x2,, x2 ,)

để cho những ai vẫn đang tìm kiếm, lớp này: http://www.codeguru.com/cpp/tic/tic0226.shtml là khá mát mẻ ... một số thay đổi có thể là cần thiết

Các vấn đề liên quan