Saya baru saja menemukan komentar dalam jawaban ini yang mengatakan bahwa menggunakan iostream::eof
dalam kondisi loop adalah "hampir pasti salah". Saya biasanya menggunakan sesuatu seperti while(cin>>n)
- yang saya kira secara implisit memeriksa EOF.
Mengapa memeriksa bukti penggunaan while (!cin.eof())
salah secara eksplisit ?
Apa bedanya dengan menggunakan scanf("...",...)!=EOF
C (yang sering saya gunakan tanpa masalah)?
scanf(...) != EOF
juga tidak akan berfungsi di C, karenascanf
mengembalikan jumlah bidang yang berhasil diuraikan dan ditugaskan. Kondisi yang benar adalah discanf(...) < n
manan
jumlah bidang dalam string format.EOF
jika akhir file ditemukan sebelum konversi bidang pertama (berhasil atau tidak). Jika akhir file dicapai di antara bidang, itu akan mengembalikan jumlah bidang yang berhasil dikonversi dan disimpan. Yang membuat perbandingan dengan yangEOF
salah..eof()
setelah loop keluar.while(fail)
loop berakhir dengan kegagalan aktual dan bukti. Pikirkan jika Anda memerlukan 3 int per iterasi (katakanlah Anda membaca titik xyz atau sesuatu), tetapi ada, secara keliru, hanya dua int dalam stream.Jawaban:
Karena
iostream::eof
hanya akan kembalitrue
setelah membaca akhir aliran. Itu tidak menunjukkan, bahwa bacaan berikutnya akan menjadi akhir dari aliran.Pertimbangkan ini (dan anggap bacaan selanjutnya akan berada di akhir arus):
Terhadap ini:
Dan pada pertanyaan kedua Anda: Karena
sama dengan
dan tidak sama dengan
sumber
int
s ataustd::string
s atau serupa, sedikit EOF adalah mengatur kapan Anda ekstrak yang tepat sebelum akhir dan ekstraksi hits akhir. Anda tidak perlu membaca lagi. Alasan tidak diatur saat membaca dari file adalah karena ada tambahan\n
di akhir. Saya sudah membahas ini dalam jawaban lain . Membacachar
s adalah masalah yang berbeda karena hanya mengekstrak satu per satu dan tidak terus mencapai akhir.while (!eof())
itu tidak akan "bekerja" padaint
ataustd::string
ketika input benar-benar kosong, sehingga bahkan mengetahui tidak ada\n
perawatan trailing diperlukan."Hello"
(tidak ada spasi spasi atau atau\n
) danstd::string
diekstraksi, itu akan mengekstrak surat dariH
keo
, berhenti mengekstraksi, dan maka tidak mengatur bit EOF. Bahkan, itu akan mengatur bit EOF karena EOF yang menghentikan ekstraksi. Hanya berharap untuk menjernihkan hal itu bagi orang-orang.Bottom-line top: Dengan penanganan ruang putih yang benar, berikut ini adalah bagaimana
eof
dapat digunakan (dan bahkan, lebih dapat diandalkan daripadafail()
untuk pengecekan kesalahan):( Terima kasih Tony D atas saran untuk menyoroti jawabannya. Lihat komentarnya di bawah ini untuk contoh mengapa ini lebih kuat. )
Argumen utama menentang penggunaan
eof()
tampaknya tidak ada seluk beluk penting tentang peran ruang putih. Proposisi saya adalah bahwa, memeriksaeof()
secara eksplisit tidak hanya bukan " selalu salah " - yang tampaknya menjadi pendapat utama dalam hal ini dan utas SO yang serupa -, tetapi dengan penanganan ruang putih yang tepat, ini memberikan pembersih dan lebih dapat diandalkan penanganan kesalahan, dan merupakan solusi yang selalu benar (walaupun, belum tentu yang tersest).Untuk meringkas apa yang disarankan sebagai penghentian dan baca "benar" adalah sebagai berikut:
Kegagalan karena upaya membaca di luar bukti diambil sebagai kondisi penghentian. Ini berarti bahwa tidak ada cara mudah untuk membedakan antara aliran yang berhasil dan yang benar-benar gagal karena alasan selain eof. Ambil aliran berikut:
1 2 3 4 5<eof>
1 2 a 3 4 5<eof>
a<eof>
while(in>>data)
berakhir dengan satu setfailbit
untuk semua tiga masukan. Di bagian pertama dan ketiga,eofbit
juga diatur. Jadi melewati loop kita perlu logika ekstra yang sangat buruk untuk membedakan input yang tepat (1) dari yang tidak tepat (2 dan 3).Sedangkan, ambil yang berikut:
Di sini,
in.fail()
memverifikasi bahwa selama ada sesuatu untuk dibaca, itu adalah yang benar. Tujuannya bukanlah terminator loop sementara.Sejauh ini bagus, tapi apa yang terjadi jika ada ruang tambahan di sungai - apa yang terdengar seperti kekhawatiran utama
eof()
sebagai terminator?Kami tidak perlu menyerahkan penanganan kesalahan kami; cukup makan ruang putih:
std::ws
melompati potensi (nol atau lebih) spasi tambahan di aliran saat mengatureofbit
, dan bukanfailbit
. Jadi,in.fail()
berfungsi seperti yang diharapkan, selama setidaknya ada satu data untuk dibaca. Jika aliran semua-kosong juga dapat diterima, maka formulir yang benar adalah:Ringkasan: Dibuat dengan benar
while(!eof)
tidak hanya mungkin dan tidak salah, tetapi memungkinkan data untuk dilokalisasi dalam ruang lingkup, dan menyediakan pemisahan yang lebih bersih dari pengecekan kesalahan dari bisnis seperti biasa. Yang sedang berkata,while(!fail)
mungkin idiom yang lebih umum dan singkat, dan mungkin lebih disukai dalam skenario sederhana (data tunggal per jenis baca).sumber
eofbit
danfailbit
diatur, yang lain hanyafailbit
diatur. Anda hanya perlu menguji bahwa sekali setelah loop telah berakhir, bukan pada setiap iterasi; itu hanya akan meninggalkan loop sekali, jadi Anda hanya perlu memeriksa mengapa itu meninggalkan loop sekali.while (in >> data)
berfungsi dengan baik untuk semua aliran kosong.!eof & fail
loop masa lalu. Ada kasus di mana seseorang tidak bisa mengandalkan ini. Lihat komentar di atas ( goo.gl/9mXYX ). Bagaimanapun, saya tidak mengusulkaneof
-ceck sebagai alternatif yang selalu lebih baik . Saya hanya mengatakan, itu adalah cara yang mungkin dan (dalam beberapa kasus lebih tepat) untuk melakukan ini, daripada "pasti salah!" karena cenderung diklaim di sini di SO.stream >> my_int
di mana sungai mengandung misalnya "-":eofbit
danfailbit
yang set. Itu lebih buruk daripadaoperator>>
skenario, di mana kelebihan yang disediakan pengguna setidaknya memiliki opsi untuk membersihkaneofbit
sebelum kembali untuk membantu mendukungwhile (s >> x)
penggunaan. Secara umum, jawaban ini bisa menggunakan pembersihan - hanya finalwhile( !(in>>ws).eof() )
yang kuat, dan akhirnya terkubur.Karena jika programmer tidak menulis
while(stream >> n)
, mereka mungkin menulis ini:Di sini masalahnya adalah, Anda tidak dapat melakukannya
some work on n
tanpa terlebih dahulu memeriksa apakah aliran membaca berhasil, karena jika tidak berhasil, Andasome work on n
akan menghasilkan hasil yang tidak diinginkan.Seluruh titik adalah bahwa,
eofbit
,badbit
, ataufailbit
ditetapkan setelah dilakukan usaha untuk membaca dari sungai. Jadi jikastream >> n
gagal, makaeofbit
,,badbit
ataufailbit
diatur segera, jadi lebih idiomatis jika Anda menuliswhile (stream >> n)
, karena objek yang dikembalikanstream
dikonversi kefalse
jika ada beberapa kegagalan dalam membaca dari aliran dan akibatnya loop berhenti. Dan itu dikonversi menjaditrue
jika pembacaan berhasil dan perulangan berlanjut.sumber
n
, program mungkin juga jatuh ke loop tak terbatas , jika operasi aliran yang gagal tidak mengkonsumsi input apa pun.Jawaban lain telah menjelaskan mengapa logika salah
while (!stream.eof())
dan cara memperbaikinya. Saya ingin fokus pada sesuatu yang berbeda:Secara umum, memeriksa
eof
hanya salah karena ekstraksi aliran (>>
) dapat gagal tanpa mengenai akhir file. Jika Anda memiliki egint n; cin >> n;
dan stream berisihello
, makah
itu bukan digit yang valid, jadi ekstraksi akan gagal tanpa mencapai akhir input.Masalah ini, dikombinasikan dengan kesalahan logika umum saat memeriksa keadaan aliran sebelum mencoba membaca darinya, yang berarti untuk item input N loop akan berjalan N + 1 kali, mengarah ke gejala berikut:
Jika aliran kosong, loop akan berjalan sekali.
>>
akan gagal (tidak ada input untuk dibaca) dan semua variabel yang seharusnya disetelstream >> x
sebenarnya tidak diinisialisasi. Ini mengarah pada data sampah yang sedang diproses, yang dapat bermanifestasi sebagai hasil yang tidak masuk akal (seringkali jumlahnya sangat besar).(Jika pustaka standar Anda sesuai dengan C ++ 11, segalanya agak berbeda sekarang: Yang gagal
>>
sekarang menetapkan variabel numerik0
alih-alih membiarkannya tidak diinisialisasi (kecuali untukchar
s).)Jika aliran tidak kosong, loop akan berjalan lagi setelah input yang valid terakhir. Karena dalam iterasi terakhir semua
>>
operasi gagal, variabel cenderung mempertahankan nilainya dari iterasi sebelumnya. Ini dapat bermanifestasi sebagai "baris terakhir dicetak dua kali" atau "catatan input terakhir diproses dua kali".(Ini seharusnya bermanifestasi sedikit berbeda sejak C ++ 11 (lihat di atas): Sekarang Anda mendapatkan "catatan hantu" dari nol alih-alih baris terakhir yang diulang.)
Jika aliran berisi data salah tetapi Anda hanya memeriksa
.eof
, Anda berakhir dengan loop tak terbatas.>>
akan gagal mengekstraksi data apa pun dari arus, sehingga loop berputar di tempatnya tanpa pernah mencapai akhir.Untuk rekap: Solusinya adalah untuk menguji keberhasilan
>>
operasi itu sendiri, tidak menggunakan terpisah.eof()
metode:while (stream >> n >> m) { ... }
, seperti di C Anda menguji keberhasilanscanf
panggilan itu sendiri:while (scanf("%d%d", &n, &m) == 2) { ... }
.sumber