2010-02-26 26 views
12

Tôi có dữ liệu theo định dạng sau:dữ liệu đọc được định dạng với C++ 's điều hành dòng >> khi dữ liệu có không gian

 
4:How do you do? 
10:Happy birthday 
1:Purple monkey dishwasher 
200:The Ancestral Territorial Imperatives of the Trumpeter Swan 

Số có thể là bất cứ nơi nào 1-999, và chuỗi có chiều dài tối đa là 255 ký tự . Tôi mới dùng C++ và có vẻ như một vài nguồn khuyên bạn nên trích xuất dữ liệu có định dạng bằng toán tử >> của luồng, nhưng khi tôi muốn trích xuất chuỗi, nó dừng lại ở ký tự khoảng trắng đầu tiên. Có cách nào để định cấu hình luồng để ngừng phân tích cú pháp một chuỗi chỉ ở dòng mới hoặc cuối tệp không? Tôi thấy rằng có một phương pháp getline để trích xuất toàn bộ một dòng, nhưng sau đó tôi vẫn phải chia nó theo cách thủ công [với find_first_of], phải không?

Có cách nào dễ dàng để phân tích dữ liệu ở định dạng này bằng chỉ sử dụng STL không?

+2

Streams trong C++ là một trong những điều tôi ghét về C++. – AraK

+0

Khi tôi mới bắt đầu với C++, tôi đã hy vọng rằng các luồng là một trong những điều cuối cùng dẫn đến một biểu hiện của "oooooh thông minh" nhưng sau khi bình luận của bạn, tôi bắt đầu nghĩ rằng điều đó sẽ không bao giờ xảy ra. :( – dreamlax

Trả lời

10

Bạn có thể đọc số trước khi sử dụng std::getline, đọc từ luồng và lưu trữ thành đối tượng std::string. Một cái gì đó như thế này:

int num; 
string str; 

while(cin>>num){ 
    getline(cin,str); 

} 
+0

Điều đó có vẻ sạch sẽ, tôi cho rằng sẽ an toàn khi thay thế 'cin' bằng' istream' mà tôi đã đưa ra? – dreamlax

+0

Nếu bạn đang đọc từ một tệp, bạn có thể thay thế cin bằng a ** đối tượng ifstream hợp lệ – codaddict

+0

Tôi chỉ được cung cấp luồng và mã của tôi được dự kiến ​​sẽ phân tích cú pháp dữ liệu, thao tác và ghi nó vào luồng khác. Tôi không tạo luồng. Tôi giả định bộ lọc của mình sẽ Không được gọi nếu 'istream' hoặc' ostream' không hợp lệ, nhưng tại cùng một thời gian tôi không nghĩ rằng đó là bất kỳ mối quan tâm của tôi. Rác trong rác ra :). . . hoặc có thể rác trong segfault ra. – dreamlax

2
int i; 
char *string = (char*)malloc(256*sizeof(char)); //since max is 255 chars, and +1 for '\0' 
scanf("%d:%[^\n]s",&i, string); //use %255[^\n]s for accepting 255 chars max irrespective of input size 
printf("%s\n", string); 
 

C và cũng sẽ hoạt động trong C++. scanf cung cấp khả năng kiểm soát nhiều hơn, nhưng không quản lý lỗi. Vì vậy, sử dụng thận trọng :).

+0

Có vẻ như cờ 'm' không được tiêu chuẩn hóa, vì vậy tôi không thể sử dụng nó. Nhưng, một lần nữa, điều này sẽ không chỉ đọc cho ký tự khoảng trống đầu tiên thay vì đến cuối dòng không? – dreamlax

+0

vẫn chỉ đọc từ đầu tiên của dòng, không phải toàn bộ dòng, và bạn có lỗi trong mã của mình: bạn đang cung cấp 'i' nhưng' scanf' cần một con trỏ * thành 'i' (' & i'). – dreamlax

+0

@dreamlax Cảm ơn bạn đã chỉ ra Đã sửa lỗi –

2

Chỉ cần đọc dòng dữ liệu theo dòng (toàn bộ dòng) bằng cách sử dụng getline và phân tích cú pháp.
Để phân tích sử dụng find_first_of()

+0

bạn có thể cung cấp một số mã mẫu không? – Sergei

9

Bạn đã được thông báo về std::getline, nhưng họ đã không đề cập đến một chi tiết mà có thể bạn sẽ tìm thấy hữu ích: khi bạn gọi getline, bạn cũng có thể vượt qua một tham số nói nó những gì nhân vật để điều trị như là kết thúc của đầu vào. Để đọc số điện thoại, bạn có thể sử dụng:

std::string number; 
std::string name; 

std::getline(infile, number, ':'); 
std::getline(infile, name); 

này sẽ đưa dữ liệu lên đến ':' vào number, loại bỏ các ':', và đọc phần còn lại của dòng vào name.

Nếu bạn muốn sử dụng >> để đọc dữ liệu, bạn cũng có thể làm điều đó, nhưng khó hơn một chút và đi sâu vào một khu vực của thư viện chuẩn mà hầu hết mọi người không bao giờ chạm vào. Một luồng có một liên kết locale được sử dụng cho những thứ như số định dạng và (quan trọng) xác định những gì cấu thành "khoảng trắng". Bạn có thể xác định miền địa phương của mình để xác định ":" làm khoảng trắng, và khoảng trống ("") là không phải là khoảng trắng. Yêu cầu luồng sử dụng ngôn ngữ đó và nó sẽ cho phép bạn đọc dữ liệu trực tiếp.

#include <locale> 
#include <vector> 

struct colonsep: std::ctype<char> { 
    colonsep(): std::ctype<char>(get_table()) {} 

    static std::ctype_base::mask const* get_table() { 
     static std::vector<std::ctype_base::mask> 
      rc(std::ctype<char>::table_size,std::ctype_base::mask()); 

     rc[':'] = std::ctype_base::space; 
     rc['\n'] = std::ctype_base::space; 
     return &rc[0]; 
    } 
}; 

Bây giờ sử dụng nó, chúng tôi "thấm nhuần" dòng với một ngôn ngữ:

#include <fstream> 
#include <iterator> 
#include <algorithm> 
#include <iostream> 

typedef std::pair<int, std::string> data; 

namespace std { 
    std::istream &operator>>(std::istream &is, data &d) { 
     return is >> d.first >> d.second; 
    } 
    std::ostream &operator<<(std::ostream &os, data const &d) { 
     return os << d.first << ":" << d.second; 
    } 
} 

int main() { 
    std::ifstream infile("testfile.txt"); 
    infile.imbue(std::locale(std::locale(), new colonsep)); 

    std::vector<data> d; 

    std::copy(std::istream_iterator<data>(infile), 
       std::istream_iterator<data>(), 
       std::back_inserter(d)); 

    // just for fun, sort the data to show we can manipulate it: 
    std::sort(d.begin(), d.end()); 

    std::copy(d.begin(), d.end(), std::ostream_iterator<data>(std::cout, "\n")); 
    return 0; 
} 

Bây giờ bạn biết lý do tại sao rằng một phần của thư viện được nên bỏ qua. Về lý thuyết, việc có được thư viện chuẩn để làm công việc của bạn cho bạn là rất tốt - nhưng thực tế, hầu hết thời gian bạn dễ dàng thực hiện loại công việc này hơn thay cho bạn.

+0

Trực tiếp để tìm hiểu! Cảm ơn, Jerry! –

13

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" 

int main() 
{ 
    struct line_type 
    { 
     unsigned int id; 
     std::string str; 
    }; 

    std::deque<line_type> line_list; 

    const std::string file_name = "data.txt"; 

    strtk::for_each_line(file_name, 
         [&line_list](const std::string& line) 
         { 
          line_type temp_line; 
          const bool result = strtk::parse(line, 
                  ":", 
                  temp_line.id, 
                  temp_line.str); 
          if (!result) return; 
          line_list.push_back(temp_line); 
         }); 

    return 0; 
} 

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

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