2008-11-06 67 views
8

Tôi có một tập tin với dữ liệu được liệt kê như sau:C++ tập tin io & tách bởi dấu phân cách

0,  2, 10 
10,  8, 10 
10,  10, 10 
10,  16, 10 
15,  10, 16 
17,  10, 16 

Tôi muốn để có thể nhập vào các tập tin và chia nó thành ba mảng, trong quá trình cắt tỉa tất cả các không gian dư thừa và chuyển đổi từng phần tử thành số nguyên.

Vì một lý do nào đó, tôi không thể tìm thấy cách dễ dàng để thực hiện việc này trong C++. Thành công duy nhất tôi có là bằng cách nhập mỗi dòng vào một mảng và sau đó loại bỏ tất cả các khoảng trắng và sau đó tách nó ra. Toàn bộ quá trình này đã cho tôi 20-30 dòng mã tốt và nỗi đau của nó để sửa đổi để nói một dấu tách khác (ví dụ: dấu cách), v.v.

Đây là tương đương python của những gì tôi muốn có trong C++:

f = open('input_hard.dat') 
lines = f.readlines() 
f.close() 

#declarations 
inint, inbase, outbase = [], [], [] 

#input parsing 
for line in lines: 
    bits = string.split(line, ',') 
    inint.append(int(bits[0].strip())) 
    inbase.append(int(bits[1].strip())) 
    outbase.append(int(bits[2].strip())) 

Dễ sử dụng khi làm điều này trong python là một trong những lý do khiến tôi chuyển nó ngay từ đầu. Tuy nhiên, tôi yêu cầu để làm điều này trong C + + bây giờ và tôi sẽ ghét phải sử dụng mã số 20-30 xấu xí của tôi.

Bất kỳ trợ giúp nào sẽ được đánh giá cao, cảm ơn!

Trả lời

4

Thực sự không có gì sai với fscanf, có lẽ là giải pháp nhanh nhất trong trường hợp này. Và nó ngắn và dễ đọc như mã python:

FILE *fp = fopen("file.dat", "r"); 
int x, y, z; 
std::vector<int> vx, vy, vz; 

while (fscanf(fp, "%d, %d, %d", &x, &y, &z) == 3) { 
    vx.push_back(x); 
    vy.push_back(y); 
    vz.push_back(z); 
} 
fclose(fp); 
+0

Công việc tuyệt vời. Folks quên về mô hình phù hợp của scanf. Giải pháp đơn giản nhất là giải pháp tốt nhất. – jbruni

2

Cái gì như:

vector<int> inint; 
vector<int> inbase; 
vector<int> outbase; 
while (fgets(buf, fh)) { 
    char *tok = strtok(buf, ", "); 
    inint.push_back(atoi(tok)); 
    tok = strtok(NULL, ", "); 
    inbase.push_back(atoi(tok)); 
    tok = strtok(NULL, ", "); 
    outbase.push_back(atoi(tok)); 
} 

Trừ với việc kiểm tra lỗi.

+0

tôi sẽ tránh một giải pháp "C-ish" như cho, tốt, thẩm mỹ ... nhưng quan trọng hơn trong trường hợp này bởi vì strtok có một số vấn đề an toàn chủ đề nghiêm trọng. Mã đúng mặc dù! – MattyT

1

std :: getline cho phép bạn đọc một dòng văn bản, và bạn có thể sử dụng một dòng chuỗi để phân tích dòng cá nhân:

string buf; 
getline(cin, buf); 
stringstream par(buf); 

char buf2[512]; 
par.getline(buf2, 512, ','); /* Reads until the first token. */ 

khi bạn nhận được dòng văn bản thành chuỗi, bạn có thể thực sự sử dụng bất kỳ hàm phân tích nào bạn muốn, ngay cả sscanf (buf.c_str(), "% d,% d '% d", & i1, & i2, & i3), bằng cách sử dụng atoi trên chuỗi con với số nguyên hoặc thông qua một số phương pháp khác.

Bạn cũng có thể bỏ qua ký tự không mong muốn trong dòng đầu vào, nếu bạn biết họ đang có:

if (cin.peek() == ',') 
    cin.ignore(1, ','); 
cin >> nextInt; 
1

Nếu bạn không nhớ bằng cách sử dụng thư viện Boost ...

#include <string> 
#include <vector> 
#include <boost/lexical_cast.hpp> 
#include <boost/regex.hpp> 

std::vector<int> ParseFile(std::istream& in) { 
    const boost::regex cItemPattern(" *([0-9]+),?"); 
    std::vector<int> return_value; 

    std::string line; 
    while (std::getline(in, line)) { 
     string::const_iterator b=line.begin(), e=line.end(); 
     boost::smatch match; 
     while (b!=e && boost::regex_search(b, e, match, cItemPattern)) { 
      return_value.push_back(boost::lexical_cast<int>(match[1].str())); 
      b=match[0].second; 
     }; 
    }; 

    return return_value; 
} 

Điều đó kéo các dòng từ luồng, sau đó sử dụng thư viện Boost :: RegEx (với một nhóm chụp) để trích xuất từng số từ các dòng. Nó tự động bỏ qua bất cứ điều gì đó không phải là một số hợp lệ, mặc dù điều đó có thể được thay đổi nếu bạn muốn.

Nó vẫn còn khoảng hai mươi dòng với #include s, nhưng bạn có thể sử dụng nó để trích xuất về cơ bản mọi thứ từ các dòng của tệp. Đây là một ví dụ nhỏ, tôi đang sử dụng khá nhiều mã giống hệt nhau để trích xuất các thẻ và các giá trị tùy chọn từ một trường cơ sở dữ liệu, sự khác biệt lớn duy nhất là biểu thức chính quy.

EDIT: Rất tiếc, bạn muốn có ba vectơ riêng biệt.Hãy thử thay đổi một chút này để thay thế:

const boost::regex cItemPattern(" *([0-9]+), *([0-9]+), *([0-9]+)"); 
std::vector<int> vector1, vector2, vector3; 

std::string line; 
while (std::getline(in, line)) { 
    string::const_iterator b=line.begin(), e=line.end(); 
    boost::smatch match; 
    while (b!=e && boost::regex_search(b, e, match, cItemPattern)) { 
     vector1.push_back(boost::lexical_cast<int>(match[1].str())); 
     vector2.push_back(boost::lexical_cast<int>(match[2].str())); 
     vector3.push_back(boost::lexical_cast<int>(match[3].str())); 
     b=match[0].second; 
    }; 
}; 
6

Không có nhu cầu thực sự sử dụng tăng trong ví dụ này là suối sẽ làm các trick độc đáo:

int main(int argc, char* argv[]) 
{ 
    ifstream file(argv[1]); 

    const unsigned maxIgnore = 10; 
    const int delim = ','; 
    int x,y,z; 

    vector<int> vecx, vecy, vecz; 

    while (file) 
    { 
     file >> x; 
     file.ignore(maxIgnore, delim); 
     file >> y; 
     file.ignore(maxIgnore, delim); 
     file >> z; 

     vecx.push_back(x); 
     vecy.push_back(y); 
     vecz.push_back(z); 
    } 
} 

Mặc dù nếu tôi được là sẽ sử dụng tăng Tôi muốn sự đơn giản của tokenizer đến regex ... :)

1

tại sao không cùng mã với python :)?

std::ifstream file("input_hard.dat"); 
std::vector<int> inint, inbase, outbase; 

while (file.good()){ 
    int val1, val2, val3; 
    char delim; 
    file >> val1 >> delim >> val2 >> delim >> val3; 

    inint.push_back(val1); 
    inbase.push_back(val2); 
    outbase.push_back(val3); 
} 
0

Nếu bạn muốn có thể mở rộng đến định dạng đầu vào khó hơn, bạn nên cân nhắc tinh thần, tăng thư viện bộ phối hợp phân tích cú pháp.

This page có một ví dụ mà gần như làm những gì bạn cần (với số thực và một véc tơ mặc dù)

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