2012-01-21 31 views
7

Mã của tôi đọc các biến int chưa được ký từ tệp văn bản Input_File_Name.Làm thế nào để đọc các biến int chưa được gán từ tệp một cách chính xác, sử dụng ifstream?

unsigned int Column_Count; //Cols 
unsigned int Row_Count;//Rows 

try { 
    ifstream input_stream; 
    input_stream.open(Input_File_Name,ios_base::in); 
    if (input_stream) { 
     //if file is opened 
     input_stream.exceptions(ios::badbit | ios::failbit); 
     input_stream>>Row_Count; 
     input_stream>>Column_Count; 


    } else { 
     throw std::ios::failure("Can't open input file"); 
     //cout << "Error: Can't open input file" << endl; 
    } 

} catch (const ios::failure& error) { 
    cout << "Oh No!!" << error.what() << endl;   
} catch (const exception& error) { 
    cout << error.what() <<"Oh No!!" << endl; 
} catch (...) { 
    cout << "Unknown exception" << endl; 
} 

Nó hoạt động tuyệt vời. Nhưng khi tôi điền vào tập tin văn bản với một dữ liệu sai

33abcd4 567fg8 

Nó hoạt động theo cách như vậy:

input_stream>>Row_Count; //Row_Count = 33; 
input_stream>>Column_Count; // throws an ios::failure exception 

Tại sao không này dòng input_stream>>Row_Count; ném ngoại lệ? Như tôi đã hiểu, input_stream xem xét bất kỳ ký tự không phải là số nào làm dấu phân cách và bước tiếp theo nó cố đọc "abcd". Có phải vậy không? Cách đặt ký hiệu Dấu cách làm dấu phân tách để ném một ngoại lệ ios::failure từ dòng mã này input_stream>>Row_Count; trong khi đọc "33abcd4"?

+1

Đó chỉ là cách toán tử >> hoạt động; nó sẽ trích xuất '33' và sau đó dừng lại, 'abcd' vẫn ở trong luồng cho cuộc gọi tiếp theo tới 'toán tử >>'. Thay vào đó, bạn có thể đọc '33abcd4' thành một chuỗi và sau đó kiểm tra các ký tự không phải số trong đó. Ngoài ra, nếu bạn có trình biên dịch gần đây hỗ trợ C++ 11, hãy kiểm tra xem thư viện chuẩn có cung cấp 'std :: stoull' hay không. – jrok

Trả lời

4

Việc trích xuất bình thường giá trị số nguyên thành công nếu luồng có thể đọc bất kỳ giá trị số nguyên nào. Đó là, nếu có ít nhất một chữ số tùy chọn theo sau bởi bất cứ điều gì đọc của một số nguyên thành công. Các hoạt động khai thác thông thường không cố gắng đọc thêm, đặc biệt là chúng không cố gắng tìm khoảng trống tiếp theo.

Từ âm thanh của nó, bạn muốn chắc chắn rằng có một khoảng trống sau số của bạn và thất bại nếu không có. Tôi có thể nghĩ ra hai cách tiếp cận khác nhau để thực hiện việc này:

  1. Tạo một trình xử lý đơn giản để kiểm tra luồng trên ký tự khoảng trắng. Điều này, tuy nhiên, có nghĩa là bạn sẽ đọc các giá trị của bạn bằng cách sử dụng một cái gì đó như in >> value >> is_space.
  2. Tạo một tùy chỉnh std::num_get<char> khía cạnh, cài đặt nó thành một std::localeimbue() này std::locale vào (các) luồng của bạn. Nó có liên quan nhiều hơn một chút nhưng không yêu cầu bất kỳ thay đổi nào về cách đọc số nguyên.

Tạo một thao túng như thế này là khá tầm thường:

std::istream& is_space(std::istream& in) 
{ 
    if (!std::isspace(in.peek())) 
    { 
     in.setstate(std::ios_base::failbit); 
    } 
    return in; 
} 

Bây giờ, thay đổi số cách được đọc là thú vị hơn và tôi nghi ngờ tôi vừa mới đặt tên một số lớp thư viện chuẩn hầu hết mọi người là khá không nhận thức được. Vì vậy, hãy nhanh chóng loại ra một ví dụ cho điều này là tốt. Tôi sẽ thay đổi các khía cạnh std::num_get<char> chỉ để đối phó với unsigned int: để làm điều đó cho các loại tích phân khác, nó là cần thiết để ghi đè lên nhiều chức năng hơn.Vì vậy, đây là một sự thay thế cho std::num_get<char> khía cạnh:

class num_get: 
    public std::num_get<char> 
{ 
    iter_type do_get(iter_type it, iter_type end, 
        std::ios_base& ios, std::ios_base::iostate& err, 
        unsigned int& value) const 
    { 
     it = std::num_get<char>::do_get(it, end, ios, err, value); 
     if (it != end && !isspace(static_cast<unsigned char>(*it))) 
     { 
      err |= std::ios_base::failbit; 
     } 
     return it; 
    } 
}; 

Tất cả điều này làm là để lấy được một lớp từ std::num_get<char> và ghi đè lên một trong những chức năng ảo của nó. Việc thực hiện hàm này khá thẳng về phía trước: bắt đầu bằng cách đọc giá trị bằng cách ủy nhiệm cho lớp cơ sở (tôi vừa nhận ra rằng các hàm ảo thực sự muốn bảo vệ chứ không phải riêng tư như trước đây nhưng đây là một thảo luận hoàn toàn khác) . Độc lập cho dù điều này đã thành công (nếu không, nó sẽ thiết lập một trạng thái lỗi trong err) kiểm tra ghi đè nếu có một ký tự khác có sẵn và, nếu có, hãy kiểm tra xem đó có phải là không gian không và nếu không đặt std::ios_base::failbit kết quả lỗi err.

gì còn lại là để thiết lập các dòng sử dụng khía cạnh đặc biệt này trong một std::locale và móc mới std::locale vào một dòng:

std::locale loc(std::locale(), new num_get); 
in.imbue(loc); 

Các std::locale s và khía cạnh của nó được trong nội bộ tài liệu tham khảo tính, nghĩa là bạn shouldn không theo dõi con trỏ đến khía cạnh và bạn cũng không cần giữ lại số std::locale. Nếu nó có vẻ là cồng kềnh để imbue() tạo std::locale hoặc bạn muốn sử dụng logic được sửa đổi này ở mọi nơi, bạn có thể đặt toàn cầu std::locale được sử dụng để khởi tạo bất kỳ luồng mới được tạo nào để sử dụng khía cạnh std::num_get<char> tùy chỉnh.

2

Bạn có thể làm theo cách này:

#include <iostream> 
#include <locale> 

class my_num_get : public std::num_get<char> { 
protected: 
    iter_type do_get(iter_type in, iter_type end, std::ios_base& str, std::ios_base::iostate& err, unsigned int& v) const 
    { 
     in = std::num_get<char>::do_get(in, end, str, err, v); 
     if(in != end && !std::isspace(*in, str.getloc())) 
      err |= std::ios_base::failbit; 
     return in; 
    } 
}; 

int main() { 
    using namespace std; 
    cin.imbue(std::locale(cin.getloc(), new my_num_get)); 
    cin.exceptions(ios_base::badbit | ios_base::failbit); 
    try { 
     unsigned int x; 
     cin >> x; 
    } catch(const std::exception &e) { 
     cerr << e.what() << "\n"; 
    } 
} 

Nếu bạn muốn điều này để làm việc với nhiều loại khác nữa, sau đó thực hiện như sau trong cùng một cách:

iter_type do_get(iter_type, iter_type, ios_base&, ios_base::iostate&, T& v) const 

nơi T là một trong những bool, long, long long, unsigned short, unsigned long, unsigned long long, float, double, long doublevoid*.

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