2009-08-24 14 views
18

tôi cần phải đọc trong các tập tin dữ liệu mà trông như thế này:Làm thế nào để đọc các số từ một tập tin ASCII (C++)

* SZA: 10.00 
2.648 2.648 2.648 2.648 2.648 2.648 2.648 2.649 2.650 2.650 
2.652 2.653 2.652 2.653 2.654 2.654 2.654 2.654 2.654 2.654 
2.654 2.654 2.654 2.655 2.656 2.656 2.657 2.657 2.657 2.656 
2.656 2.655 2.655 2.653 2.653 2.653 2.654 2.658 2.669 2.669 
2.667 2.666 2.666 2.664 2.663 2.663 2.663 2.662 2.663 2.663 
2.663 2.663 2.663 2.663 2.662 2.660 2.656 2.657 2.657 2.657 
2.654 2.653 2.652 2.651 2.648 2.647 2.646 2.642 2.641 2.637 
2.636 2.636 2.634 2.635 2.635 2.635 2.635 2.634 2.633 2.633 
2.633 2.634 2.634 2.635 2.637 2.638 2.637 2.639 2.640 2.640 
2.639 2.640 2.640 2.639 2.639 2.638 2.640 2.640 2.638 2.639 
2.638 2.638 2.638 2.638 2.637 2.637 2.637 2.634 2.635 2.636 
2.637 2.639 2.641 2.641 2.643 2.643 2.643 2.642 2.643 2.642 
2.641 2.642 2.642 2.643 2.645 2.645 2.645 2.645 

Điều gì sẽ là cách thanh lịch nhất để đọc file này vào một mảng phao?

Tôi biết cách đọc từng dòng đơn thành một chuỗi và tôi biết cách chuyển đổi chuỗi thành nổi bằng cách sử dụng atof(). Nhưng làm thế nào để tôi làm phần còn lại dễ nhất?

Tôi đã nghe nói về bộ đệm chuỗi, điều này có thể giúp tôi không?

Trả lời

11

Vì thẻ này được gắn thẻ là C++ nên cách rõ ràng nhất là sử dụng luồng. Trên đầu của tôi, một cái gì đó như thế này có thể làm:

std::vector<float> readFile(std::istream& is) 
{ 
    char chdummy; 
    is >> std::ws >> chdummy >> std::ws; 
    if(!is || chdummy != '*') error(); 
    std::string strdummy; 
    std::getline(is,strdummy,':'); 
    if(!is || strdummy != "SZA") error(); 

    std::vector<float> result; 
    for(;;) 
    { 
    float number; 
    if(!is>>number) break; 
    result.push_back(number); 
    } 
    if(!is.eof()) error(); 

    return result; 
} 

Tại sao float, BTW? Thông thường, double tốt hơn nhiều.

Sửa, vì nó đã được đặt câu hỏi liệu trả về một bản sao của vector là một ý tưởng tốt:

Đối với một giải pháp đầu tiên, tôi chắc chắn sẽ làm rõ ràng. Hàm đọc tệp vào một số vector và điều rõ ràng nhất cho hàm cần làm là trả về kết quả của nó. Cho dù kết quả này trong một sự chậm lại đáng chú ý phụ thuộc vào rất nhiều thứ (kích thước của vectơ, tần số hàm được gọi và từ đâu, tốc độ của đĩa này đọc từ đâu, cho dù trình biên dịch có thể áp dụng RVO) hay không. Tôi sẽ không muốn làm hỏng các giải pháp rõ ràng với một tối ưu hóa, nhưng nếu profiling thực sự cho thấy rằng điều này là để làm chậm, các vector nên được thông qua trong mỗi tham chiếu không const.

(Cũng lưu ý rằng C++ 1x với hỗ trợ rvalue, hy vọng sớm có sẵn bằng trình biên dịch gần bạn, sẽ kết thúc cuộc thảo luận này, vì nó sẽ ngăn không cho sao chép vectơ khi trở về từ hàm.)

+0

Vòng chung chung "đọc tất cả phao" sẽ là 'số phao; trong khi (là >> số) result.push_back (số); ' – sth

+0

Mặc dù bạn tương đương với khóa học. – sth

+0

@sth: Thật vậy, điều đó thật đáng sợ, mặc dù tôi không thích biến số "rò rỉ" ra khỏi vòng lặp. – sbi

2

tôi sẽ làm một cái gì đó như thế này:

std::ifstream input("input.txt"); 
std::vector<float> floats; 
std::string header; 
std::getline(input, header); // read in the "* SZA: 10.00" line 
if(header_is_correct(header)) { 
    float value; 
    // while we could successfully read in a float from the file... 
    while(input >> value) { 
     // store it in the vector. 
     floats.push_back(value); 
    } 
} 

LƯU Ý:header_is_correct(header) chỉ là một ví dụ, bạn sẽ cần phải thực hiện bất kỳ lỗi kiểm tra cho rằng dòng đầu tiên bằng tay ở đó.

+0

lý do tại sao downvote? Tôi đã thử nghiệm này và nó đọc chính xác mỗi phao từ tập tin vào một vector. –

18

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

#include <iostream> 
#include <string> 
#include <deque> 
#include <iterator> 

#include "strtk.hpp" 

int main() 
{ 
    std::deque<float> flist; 
    strtk::for_each_line("file.txt", 
         [&flist](const std::string& line) 
         { strtk::parse(line," ",flist); } 
         ); 
    std::copy(flist.begin(),flist.end(), 
       std::ostream_iterator<float>(std::cout,"\t")); 
    return 0; 
} 

Thêm ví dụ có thể được tìm thấy trong C++ String Toolkit (StrTk) Tokenizer.

+0

thú vị, mặc dù bạn nên rõ ràng rằng điều này chỉ dành cho C++ 0x trình biên dịch. –

+18

Rất đúng nhưng lambda chỉ có thể dễ dàng được đặt vào một vị từ kiểu struct. Tôi nghĩ về phong cách và tài liệu tham khảo trong tương lai (cho phép đối mặt với nó 1-2 năm kể từ bây giờ, mã trên và giống nhau sẽ là chuẩn) rằng sẽ là một ý tưởng tốt để có một cái nhìn khác về cách mọi thứ có thể được thực hiện. –

+12

Tôi thích điều này. Nice sử dụng lambdas mới, ngay cả khi điều này không thể là câu trả lời. –

2

giải pháp đơn giản thuật toán sử dụng STL:

#include <vector> 
#include <iostream> 
#include <string> 
#include <iterator> 

struct data 
{ 
    float first; // in case it is required, and assuming it is 
       // different from the rest 
    std::vector<float> values; 
}; 

data read_file(std::istream& in) 
{ 
    std::string tmp; 
    data d; 
    in >> tmp >> tmp >> d.first; 
    if (!in) throw std::runtime_error("Failed to parse line"); 

    std::copy(std::istream_iterator<float>(in), std::istream_iterator<float>(), 
     std::back_inserter<float>(d.values)); 

    return data; 
} 

Nếu bạn thực sự cần phải sử dụng một mảng, trước tiên bạn phải phân bổ nó (hoặc tự động hoặc tĩnh nếu bạn biết kích thước) và sau đó bạn có thể sử dụng cùng một bản sao thuật toán

// parsing the first line would be equivalent 
float data[128]; // assuming 128 elements known at compile time 
std::copy(std::istream_iterator<float>(is), std::istream_iterator<float>(), 
     data); 

Nhưng tôi sẽ khuyên bạn sử dụng std :: vector ngay cả trong trường hợp này, nếu bạn cần phải vượt qua các dữ liệu vào một chức năng mà phải mất một mảng bạn luôn có thể vượt qua nó như một con trỏ đến phần tử đầu tiên:

void f(float* data, int size); 
int main() 
{ 
    std::vector<float> v; // and populate 
    f(&v[0], v.size()); // memory is guaranteed to be contiguous 
}