2008-08-22 69 views
104

sau Mã C++ sử dụng một ifstream đối tượng để đọc các số nguyên từ một tập tin văn bản (trong đó có một số trên mỗi dòng) cho đến khi nó chạm EOF. Tại sao nó đọc số nguyên trên dòng cuối cùng hai lần? Làm thế nào để sửa lỗi này?Đọc từ file văn bản cho đến EOF lặp đi lặp lại dòng cuối cùng

Code:

#include <iostream> 
#include <fstream> 
using namespace std; 

int main() 
{ 
    ifstream iFile("input.txt"); // input.txt has integers, one per line 

    while (!iFile.eof()) 
    { 
     int x; 
     iFile >> x; 
     cerr << x << endl; 
    } 

    return 0; 
} 

INPUT.TXT:

10 
20 
30 

Output:

10 
20 
30 
30 

Lưu ý: Tôi đã bỏ qua tất cả các mã kiểm tra lỗi để giữ đoạn mã nhỏ. Hành vi trên được nhìn thấy trên Windows (Visual C++), Cygwin (gcc) và Linux (gcc).

Trả lời

109

Chỉ cần theo dõi chặt chẽ chuỗi sự kiện.

  • Grab 10
  • Grab 20
  • Grab 30
  • Grab EOF

Nhìn vào phiên thứ hai-to-ngoái. Bạn nắm lấy 30, sau đó tiếp tục kiểm tra EOF. Bạn chưa đạt được EOF vì dấu EOF chưa được đọc (nói một cách lưỡng lự), vị trí khái niệm của nó chỉ nằm sau dòng 30). Do đó bạn tiếp tục lặp lại tiếp theo. x vẫn là 30 từ lần lặp trước. Bây giờ bạn đã đọc từ luồng và bạn nhận được EOF. x vẫn còn 30 và ios :: eofbit được nâng lên. Bạn xuất ra stderr x (là 30, giống như trong lần lặp trước). Tiếp theo bạn kiểm tra EOF trong điều kiện vòng lặp, và lần này bạn đã ra khỏi vòng lặp.

Hãy thử điều này:

while (true) { 
    int x; 
    iFile >> x; 
    if(iFile.eof()) break; 
    cerr << x << endl; 
} 

Bằng cách này, có một lỗi trong mã của bạn. Bạn đã bao giờ thử chạy nó trên một tập tin trống? Hành vi bạn nhận được là vì lý do chính xác tương tự.

+40

sử dụng 'while (iFile >> x)'. Điều này đọc số nguyên và trả về luồng. Khi một luồng được sử dụng làm giá trị bool, nó sẽ kiểm tra xem luồng có hợp lệ hay không. Các phương thức hợp lệ eof() và bad() đều sai. xem http://stackoverflow.com/questions/21647/c-reading-from-text-file-until-eof-repeats-last-line#21666 –

+3

Cùng tinh thần như bình luận: thay vì 'while (true)', nó có vẻ tốt hơn để viết 'while (file.good())' – PHF

+1

Nếu bạn không chắc chắn làm thế nào để đọc tập tin cho đến khi lỗi hoặc kết thúc của tập tin, đọc bài viết này (lời giải thích rất tốt đẹp) https://gehrcke.de/2011/06/đọc-tập tin-in-c-sử dụng-ifstream-giao dịch-chính xác-với-badbit-failbit-eofbit-and-perror/ – stviper

29

Tôi thích ví dụ này, mà bây giờ, lá ra kiểm tra mà bạn có thể thêm vào bên trong khối khi:

ifstream iFile("input.txt");  // input.txt has integers, one per line 
int x; 

while (iFile >> x) 
{ 
    cerr << x << endl; 
} 

Không chắc thế nào an toàn là ...

+0

Điều gì nếu 0 là một giá trị hợp lệ, như x == 0? – harryngh

+4

@harryngh Nếu tôi hiểu chính xác, 'iFile >> x' trả về luồng, chứ không phải' x'. Luồng này sau đó được chuyển đổi hoàn toàn thành 'bool', để kiểm tra EOF. – wchargin

16

Có một cách tiếp cận khác như thế này:

#include <iterator> 
#include <algorithm> 

// ... 

    copy(istream_iterator<int>(iFile), istream_iterator<int>(), 
     ostream_iterator<int>(cerr, "\n")); 
5

Nếu không có để thay đổi nhiều của mã ban đầu, nó có thể trở thành:

while (!iFile.eof()) 
{ 
    int x; 
    iFile >> x; 
    if (!iFile.eof()) break; 
    cerr << x << endl; 
} 

nhưng tôi thích hai giải pháp khác nói chung.

2
int x; 
ifile >> x 

while (!iFile.eof()) 
{ 
    cerr << x << endl;   
    iFile >> x;  
} 
5

Mẫu EOF cần đọc chính để 'khởi động' quy trình kiểm tra EOF. Hãy xem xét các tập tin trống sẽ không ban đầu có EOF của nó thiết lập cho đến khi đọc đầu tiên. Đọc chính sẽ bắt EOF trong trường hợp này và bỏ qua vòng lặp hoàn toàn đúng cách.

Điều bạn cần nhớ ở đây là bạn không nhận được EOF cho đến lần thử đầu tiên đọc qua dữ liệu có sẵn của tệp. Việc đọc chính xác lượng dữ liệu sẽ không gắn cờ EOF.

Tôi nên chỉ ra nếu tệp trống mà mã đã cho của bạn sẽ được in vì EOF sẽ ngăn không cho giá trị được đặt thành x khi nhập vào vòng lặp.

Vì vậy, thêm một đọc thủ và di chuyển của vòng lặp đọc đến cùng:

int x; 

iFile >> x; // prime read here 
while (!iFile.eof()) { 
    cerr << x << endl; 
    iFile >> x; 
} 
2

Vào cuối dòng cuối cùng, bạn có một ký tự dòng mới, không được đọc bởi >> toán tử và nó không phải là một kết thúc của tập tin. Vui lòng tạo một thử nghiệm và xóa dòng mới (ký tự đại diện trong tệp) - bạn sẽ không nhận được bản sao. Để có mã linh hoạt và tránh các hiệu ứng không mong muốn, hãy áp dụng bất kỳ giải pháp nào do người dùng khác đưa ra.

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