2014-10-20 15 views
7

xem xét ví dụ đơn giản sau đâyĐọc với setw: để eof hay không để eof?

#include <string> 
#include <sstream> 
#include <iomanip> 

using namespace std; 

int main() { 
    string str = "string"; 
    istringstream is(str); 
    is >> setw(6) >> str; 
    return is.eof(); 
} 

Thoạt nhìn, kể từ chiều rộng rõ ràng được xác định bởi các setw thao túng, tôi mong đợi các nhà điều hành >> để đọc xong chuỗi sau khi giải nén thành công số lượng yêu cầu của nhân vật từ luồng đầu vào. Tôi không thấy bất kỳ lý do ngay lập tức nào để cố gắng trích xuất ký tự thứ bảy, điều đó có nghĩa là tôi không mong đợi luồng sẽ nhập trạng thái eof.

Khi tôi chạy ví dụ này dưới MSVC++, nó hoạt động như tôi mong đợi: luồng vẫn ở trạng thái tốt sau khi đọc. Tuy nhiên, trong GCC, hành vi khác nhau: luồng kết thúc ở trạng thái eof.

Tiêu chuẩn ngôn ngữ, nó mang lại cho danh sách sau đây của điều kiện hoàn thành cho phiên bản này của >> hành

  • n ký tự được lưu trữ;
  • kết thúc tệp xuất hiện trên chuỗi đầu vào;
  • isspace (c, is.getloc()) là đúng cho ký tự đầu vào có sẵn tiếp theo c.

Do ở trên, tôi không thấy bất cứ lý do cho các nhà điều hành >> để lái dòng vào tình trạng eof trong đoạn code trên.

Tuy nhiên, đây là những gì >> hành implementation in GCC library trông giống như

... 
__int_type __c = __in.rdbuf()->sgetc(); 

while (__extracted < __n 
     && !_Traits::eq_int_type(__c, __eof) 
     && !__ct.is(__ctype_base::space, 
        _Traits::to_char_type(__c))) 
{ 
    if (__len == sizeof(__buf)/sizeof(_CharT)) 
    { 
    __str.append(__buf, sizeof(__buf)/sizeof(_CharT)); 
    __len = 0; 
    } 
    __buf[__len++] = _Traits::to_char_type(__c); 
    ++__extracted; 
    __c = __in.rdbuf()->snextc(); 
} 
__str.append(__buf, __len); 

if (_Traits::eq_int_type(__c, __eof)) 
    __err |= __ios_base::eofbit; 
__in.width(0); 
... 

Như bạn có thể thấy, ở phần cuối của mỗi lần lặp thành công, nó cố gắng để chuẩn bị __c ký tự tiếp theo cho phiên bản kế tiếp, mặc dù lặp lại tiếp theo có thể không bao giờ xảy ra. Và sau chu kỳ, nó phân tích giá trị cuối cùng của ký tự __c đó và đặt eofbit cho phù hợp.

Vì vậy, câu hỏi của tôi là: kích hoạt trạng thái luồng eof trong tình huống trên, như GCC thực hiện - có hợp pháp không theo quan điểm chuẩn? Tôi không thấy nó được chỉ định rõ ràng trong tài liệu. Cả hành vi của MSVC và GCC có tuân thủ không? Hay chỉ một trong số họ hoạt động chính xác?

+0

Tôi không nghĩ rằng đặc điểm kỹ thuật yêu cầu kiểm tra danh sách để đoản mạch (hoặc thậm chí theo thứ tự đó), và nếu nó không bắt buộc phải đoản mạch thì mục thứ ba (kiểm tra 'isspace' cho mục tiếp theo có sẵn ký tự đầu vào) sẽ yêu cầu nó để chuẩn bị ký tự tiếp theo. –

+0

[libC++] (http://coliru.stacked-crooked.com/a/b0c5d41b6d1944c5) đồng ý với MSVC. –

+0

Tôi không có văn bản tiêu chuẩn để sao lưu này, nhưng sự hiểu biết của tôi là nó là tùy chọn cho dù đọc ký tự cuối cùng đặt 'eofbit'. (Cố gắng đọc sau khi đọc nhân vật cuối cùng chắc chắn đã đặt nó). –

Trả lời

2

Định nghĩa cho rằng cụ thể operator>> không liên quan đến cài đặt của eofbit, vì nó chỉ mô tả khi hoạt động chấm dứt, nhưng không kích hoạt một bit cụ thể.

Mô tả cho eofbit trong tiêu chuẩn (dự thảo) nói:

eofbit - chỉ ra rằng một hoạt động đầu vào tới giai đoạn cuối của một chuỗi đầu vào;

Tôi đoán ở đây tùy thuộc vào cách bạn muốn diễn giải "đã tiếp cận".Lưu ý rằng việc thực hiện một cách chính xác gcc không đặt failbit, được định nghĩa là

failbit - chỉ ra rằng một hoạt động đầu vào thất bại để đọc các ký tự mong đợi, hoặc rằng một hoạt động ngõ ra thất bại trong việc tạo ra các ký tự mong muốn.

Vì vậy, tôi nghĩ rằng eofbit không nhất thiết có nghĩa là phần cuối của tệp sẽ cản trở việc trích xuất bất kỳ ký tự mới nào, chỉ khi kết thúc tệp đã được "đạt".

Tôi dường như không thể tìm thấy mô tả chính xác hơn cho "đã tiếp cận", vì vậy tôi đoán điều đó sẽ được thực hiện xác định. Nếu logic này đúng, thì cả hai hành vi của MSVC và gcc đều đúng.


EDIT: Đặc biệt, có vẻ như eofbit được thiết lập khi sgetc() sẽ trở eof. Điều này được mô tả cả trong phần istreambuf_iterator và trong phần basic_istream::sentry. Vì vậy, bây giờ câu hỏi là: khi nào thì vị trí hiện tại của luồng được phép thăng tiến?


EDIT cuối cùng: Hóa ra có thể là g ++ có hành vi đúng.

Mỗi lần quét ký tự đi qua <locale>, để cho phép các bộ ký tự khác nhau, định dạng tiền, mô tả thời gian và định dạng số được phân tích cú pháp. Mặc dù dường như không có mô tả nào về cách hoạt động của các chuỗi operator>>, có các mô tả rất cụ thể về cách các hàm do_get cho các số, thời gian và tiền được cho là hoạt động. Bạn có thể tìm thấy chúng từ trang 687 của dự thảo chuyển tiếp.

Tất cả những điều này bắt đầu bằng cách đọc ctype (phiên bản "toàn cầu" của một ký tự, được đọc qua miền địa phương) từ istreambuf_iterator (cho số, bạn có thể tìm định nghĩa cuộc gọi ở trang 1018 của bản nháp). Sau đó, các ctype được xử lý, và cuối cùng là iterator được nâng cao.

Vì vậy, nói chung, điều này yêu cầu trình lặp nội bộ luôn trỏ đến ký tự tiếp theo sau lần cuối cùng đọc; nếu đó không phải là trường hợp bạn có thể về mặt lý thuyết giải nén nhiều hơn bạn muốn:

string str = "strin1"; 
istringstream is(str); 
is >> setw(6) >> str; 
int x; 
is >> x; 

Nếu ký tự hiện tại cho is sau khi khai thác cho str là không phải trên eof, sau đó tiêu chuẩn sẽ đòi hỏi rằng x được giá trị 1, vì khai thác bằng số, tiêu chuẩn yêu cầu rõ ràng rằng trình vòng lặp được nâng cao sau lần đọc đầu tiên.

Vì điều này không có ý nghĩa nhiều, và cho rằng tất cả các trích xuất phức tạp được mô tả trong tiêu chuẩn hành xử theo cùng một cách, nó có ý nghĩa cho các chuỗi giống nhau sẽ xảy ra. Vì vậy, như con trỏ cho is sau khi đọc 6 ký tự rơi trên eof, cần phải đặt eofbit.

+0

Trích xuất hoặc không phải là ký tự không ảnh hưởng đến 'eof'. 'Eofbit' được đặt khi' sgetc' trả về 'eof'; tương đương khi '* it' trả về' eof', trong đó 'nó' là trình lặp dòng nội bộ. 'operator >>' * không * dừng sau khi trích xuất các ký tự 'n'. Kể từ bây giờ, trình vòng lặp luồng trỏ tới ký tự sau khi các ký tự được trích xuất và ký tự đó là 'eof', nó đặt' eofbit'. – Svalorzen