क्यों iostream :: एक लूप कंडीशन के अंदर eof (यानी `जबकि (? Stream.eof ())`) को गलत माना जाता है?


595

मुझे इस उत्तर में एक टिप्पणी मिली, जिसमें कहा गया था कि iostream::eofलूप कंडीशन में उपयोग करना "लगभग निश्चित रूप से गलत है"। मैं आम तौर पर कुछ का उपयोग while(cin>>n)करता हूं - जो मुझे लगता है कि ईओएफ के लिए अंतर्निहित जांच है।

ईओफ़ के लिए जाँच स्पष्ट रूप से while (!cin.eof())गलत का उपयोग क्यों कर रही है?

यह scanf("...",...)!=EOFC में उपयोग करने से अलग कैसे है (जो मैं अक्सर बिना किसी समस्या के उपयोग करता हूं)?


21
scanf(...) != EOFC में भी काम नहीं करेगा, क्योंकि scanfखेतों की संख्या सफलतापूर्वक पार्स और असाइन की गई है। सही स्थिति वह है scanf(...) < nजहाँ nप्रारूप स्ट्रिंग में फ़ील्ड्स की संख्या है।
बेन वोइगट

5
@Ben Voigt, यह एक नकारात्मक संख्या लौटाएगा (जो EOF आमतौर पर इस तरह के रूप में परिभाषित किया गया है) EOF तक पहुंच गया है
सेबेस्टियन

19
@ एसबेस्टियनगोडलेट: वास्तव में, यह तब वापस आएगा EOFजब फ़ाइल का अंत पहले क्षेत्र रूपांतरण (सफल या नहीं) से पहले होता है। यदि एंड-ऑफ़-फ़ाइल फ़ील्ड्स के बीच पहुँच जाता है, तो यह सफलतापूर्वक परिवर्तित और संग्रहीत फ़ील्ड की संख्या वापस कर देगा। जो EOFगलत की तुलना करता है।
बेन वोइगट

1
@ सेबेस्टियनगोडलेट: नहीं, वास्तव में नहीं। वह तब गलती करता है जब वह कहता है कि "लूप से अतीत एक अनुचित इनपुट को एक अनुचित से अलग करने का कोई (आसान) तरीका नहीं है"। वास्तव में यह .eof()लूप के बाहर निकलने के बाद की जाँच जितना आसान है।
बेन वोइगट

2
@ हाँ, इस मामले के लिए (एक साधारण इंट पढ़कर)। लेकिन एक आसानी से एक परिदृश्य के साथ आ सकता है जहां while(fail)लूप एक वास्तविक विफलता और एक ईओएफ दोनों के साथ समाप्त होता है। इस बारे में सोचें कि क्या आपको प्रति पुनरावृत्ति में 3 इंच की आवश्यकता है (जैसे कि आप एक xyz बिंदु या कुछ पढ़ रहे हैं), लेकिन वहाँ है, गलती से, धारा में केवल दो ints।
धूर्त

जवाबों:


544

क्योंकि धारा के अंत को पढ़ने के बादiostream::eof ही लौटेंगे । यह इंगित नहीं करता है, कि अगला पढ़ा स्ट्रीम का अंत होगा।true

इस पर विचार करें (और मान लें कि अगली बार स्ट्रीम के अंत में पढ़ा जाएगा):

while(!inStream.eof()){
  int data;
  // yay, not end of stream yet, now read ...
  inStream >> data;
  // oh crap, now we read the end and *only* now the eof bit will be set (as well as the fail bit)
  // do stuff with (now uninitialized) data
}

इसके खिलाफ:

int data;
while(inStream >> data){
  // when we land here, we can be sure that the read was successful.
  // if it wasn't, the returned stream from operator>> would be converted to false
  // and the loop wouldn't even be entered
  // do stuff with correctly initialized data (hopefully)
}

और आपके दूसरे प्रश्न पर: क्योंकि

if(scanf("...",...)!=EOF)

के समान है

if(!(inStream >> data).eof())

और नहीं के रूप में ही

if(!inStream.eof())
    inFile >> data

12
योग्य उल्लेख यह है कि अगर (! (InStream >> data) .eof ()) कुछ भी उपयोगी नहीं है। पतन 1: डेटा के अंतिम टुकड़े के बाद व्हॉट्सएप नहीं होने पर यह स्थिति में प्रवेश नहीं करेगा (अंतिम डेटा संसाधित नहीं होगा)। पतन 2: डेटा पढ़ने में विफल होने पर भी यह स्थिति में प्रवेश करेगा, जब तक कि ईओएफ नहीं पहुंचा था (अनंत लूप, एक ही पुराने डेटा को बार-बार संसाधित करना)।
चिरकालिक

4
मुझे लगता है कि यह इंगित करने के लायक है कि यह उत्तर थोड़ा भ्रामक है। जब निकालने intया std::stringरों या इसी तरह, EOF सा है सेट जब आप अंत से पहले एक सही निकालने और निकासी अंत करता है। आपको फिर से पढ़ने की आवश्यकता नहीं है। फ़ाइलों से पढ़ते समय इसका कारण निर्धारित नहीं होता है क्योंकि \nअंत में एक अतिरिक्त है । मैंने इसे एक अन्य उत्तर में कवर किया है । पढ़ना chars एक अलग मामला है क्योंकि यह केवल एक समय में एक निकालता है और अंत तक हिट करना जारी नहीं रखता है।
जोसफ मैन्सफील्ड

79
मुख्य समस्या यह है कि सिर्फ इसलिए कि हम ईओएफ तक नहीं पहुंचे हैं, इसका मतलब यह नहीं है कि अगला पढ़ा हुआ सफल होगा
जोसेफ मैंसफील्ड

1
@sftrabbit: यह सब सच है, लेकिन बहुत उपयोगी नहीं है ... यहां तक ​​कि अगर कोई अनुगमन '\ n' नहीं है, तो यह उचित है कि अन्य अनुगामी व्हाट्सएप को पूरे फाइल में अन्य व्हाट्सएप के साथ लगातार संभाला जाए (यानी स्किप किया गया)। इसके अलावा, की एक सूक्ष्म परिणाम "जब आप से पहले एक सही निकालने" वह यह है कि while (!eof())नहीं "काम" पर intया रों std::stringतो भी कोई अनुगामी है जानते हुए भी है जब इनपुट पूरी तरह से खाली है, \nदेखभाल की जरूरत है।
टोनी डेलरो

2
@TonyD पूरी तरह से सहमत हैं। कारण मैं इसे कह रहा हूँ है, क्योंकि मैं ज्यादातर लोगों को लगता है जब वे इस पढ़ सकते हैं और समान जवाब है कि सोचेंगे स्ट्रीम नहीं है अगर "Hello"(कोई अनुगामी खाली स्थान या \n) और एक std::stringनिकाला जाता है, यह से पत्र निकाल देंगे Hकरने के लिए o, बंद निकालने, और तब EOF बिट सेट न करें । वास्तव में, यह ईओएफ बिट सेट करेगा क्योंकि यह ईओएफ था जिसने निकासी को रोक दिया था। बस लोगों के लिए यह स्पष्ट करने की उम्मीद है।
जोसेफ मैन्सफील्ड

103

निचला-रेखा शीर्ष: श्वेत-स्थान की उचित हैंडलिंग के साथ, निम्नलिखित है कि कैसे eofइस्तेमाल किया जा सकता है (और यहां तक ​​कि, fail()जाँच की तुलना में अधिक विश्वसनीय हो ):

while( !(in>>std::ws).eof() ) {  
   int data;
   in >> data;
   if ( in.fail() ) /* handle with break or throw */; 
   // now use data
}    

( जवाब को उजागर करने के सुझाव के लिए टोनी डी का धन्यवाद। उदाहरण के लिए उनकी टिप्पणी नीचे देखें कि यह अधिक मजबूत क्यों है। )


उपयोग eof()करने के खिलाफ मुख्य तर्क सफेद स्थान की भूमिका के बारे में एक महत्वपूर्ण सूक्ष्मता याद आ रही है। मेरा प्रस्ताव यह है कि, eof()स्पष्ट रूप से जाँच करना न केवल " हमेशा गलत " है - जो कि इस और इसी तरह के थ्रेड्स में एक ओवरराइडिंग राय लगती है - लेकिन सफेद-अंतरिक्ष के उचित संचालन के साथ, यह एक क्लीनर और अधिक विश्वसनीय प्रदान करता है। त्रुटि से निपटने, और हमेशा सही समाधान है (हालांकि, जरूरी नहीं कि सबसे कठिन)।

संक्षेप में "उचित" समाप्ति और पठन आदेश के रूप में जो सुझाव दिया जा रहा है, वह निम्नलिखित है:

int data;
while(in >> data) {  /* ... */ }

// which is equivalent to 
while( !(in >> data).fail() )  {  /* ... */ }

ईओएफ से परे पढ़ने के प्रयास के कारण विफलता को समाप्ति की स्थिति के रूप में लिया जाता है। इसका मतलब यह है कि एक सफल स्ट्रीम और एक के बीच अंतर करने का कोई आसान तरीका नहीं है जो वास्तव में ईओएफ के अलावा अन्य कारणों से विफल हो जाता है। निम्नलिखित धाराएँ लें:

  • 1 2 3 4 5<eof>
  • 1 2 a 3 4 5<eof>
  • a<eof>

while(in>>data)एक सेट के साथ समाप्त हो जाता है failbitके लिए सभी तीन इनपुट। पहले और तीसरे eofbitमें भी सेट किया गया है। इसलिए पिछले लूप से एक अनुचित इनपुट (1) को अनुचित लोगों (2 और 3) से अलग करने के लिए बहुत बदसूरत अतिरिक्त तर्क की आवश्यकता होती है।

जबकि, निम्नलिखित लें:

while( !in.eof() ) 
{  
   int data;
   in >> data;
   if ( in.fail() ) /* handle with break or throw */; 
   // now use data
}    

यहां, यह in.fail()सत्यापित करता है कि जब तक कुछ पढ़ने के लिए है, यह सही है। इसका उद्देश्य केवल लूप टर्मिनेटर नहीं है।

अब तक बहुत अच्छा है, लेकिन क्या होता है यदि धारा में जगह खाली होती है - eof()टर्मिनेटर के रूप में प्रमुख चिंता की बात क्या है ?

हमें अपनी त्रुटि से निपटने के लिए आत्मसमर्पण करने की आवश्यकता नहीं है; बस सफेद अंतरिक्ष खाओ:

while( !in.eof() ) 
{  
   int data;
   in >> data >> ws; // eat whitespace with std::ws
   if ( in.fail() ) /* handle with break or throw */; 
   // now use data
}

std::wsकिसी भी संभावित (शून्य या अधिक) को स्थान पर छोड़ते समय स्ट्रीम में जगह छोड़ देता है eofbit, और नहींfailbit । इसलिए, in.fail()अपेक्षित रूप से काम करता है, जब तक कि पढ़ने के लिए कम से कम एक डेटा न हो। यदि सभी-रिक्त धाराएँ भी स्वीकार्य हैं, तो सही रूप है:

while( !(in>>ws).eof() ) 
{  
   int data;
   in >> data; 
   if ( in.fail() ) /* handle with break or throw */; 
   /* this will never fire if the eof is reached cleanly */
   // now use data
}

सारांश: एक ठीक से निर्मित while(!eof)न केवल संभव है और गलत नहीं है, लेकिन डेटा को दायरे में स्थानीयकृत करने की अनुमति देता है, और हमेशा की तरह व्यवसाय से त्रुटि की जाँच के क्लीनर जुदाई प्रदान करता है। यह कहा जा रहा है, while(!fail)inarguably एक अधिक सामान्य और प्रतिकूल मुहावरा है, और सरल (एकल डेटा प्रति पाठ प्रकार) परिदृश्यों में पसंद किया जा सकता है।


6
" तो पिछले लूप में एक अनुचित इनपुट से उचित इनपुट को अलग करने का कोई (आसान) तरीका नहीं है। " सिवाय इसके कि एक मामले में eofbitऔर दोनों failbitसेट हैं, दूसरे में केवल failbitसेट है। आपको केवल यह परीक्षण करने की आवश्यकता है कि एक बार लूप समाप्त होने के बाद, प्रत्येक पुनरावृत्ति पर नहीं; यह केवल एक बार लूप छोड़ देगा, इसलिए आपको केवल यह जांचने की आवश्यकता है कि यह एक बार लूप को क्यों छोड़ता है। while (in >> data)सभी रिक्त धाराओं के लिए ठीक काम करता है।
जोनाथन

3
आप जो कह रहे हैं (और पहले बनाया गया एक बिंदु) यह है कि एक खराब स्वरूपित धारा को !eof & failपिछले लूप के रूप में पहचाना जा सकता है । ऐसे मामले हैं जिनमें कोई इस पर भरोसा नहीं कर सकता है। उपरोक्त टिप्पणी देखें ( goo.gl/9mXYX ) या तो, मैं प्रस्ताव नहीं कर रहा हूँ हमेशा बेहतर विकल्प के eofरूप में जाँच करें । मैं केवल कह रहा हूँ, यह है एक संभव और (कुछ मामलों में अधिक उपयुक्त) ऐसा करने का तरीका, के बजाय "सबसे निश्चित रूप से गलत!" क्योंकि यह एसओ में इधर-उधर दावा किया जाता है।
धूर्त

2
"उदाहरण के लिए, पर विचार आप त्रुटियों जहां डाटा अतिभारित ऑपरेटर के साथ एक struct >> एक साथ कई क्षेत्रों को पढ़ने के लिए है, इसकी जांच होगी" - एक बहुत सरल मामले अपनी बात समर्थन कर रहा है stream >> my_intधारा जैसे जहां होता है "-": eofbitऔर failbitकर रहे हैं सेट। यह operator>>परिदृश्य से भी बदतर है, जहां उपयोगकर्ता द्वारा प्रदान किया गया अधिभार कम से कम eofbitसमर्थन while (s >> x)उपयोग में मदद करने के लिए लौटने से पहले समाशोधन का विकल्प है । अधिक आम तौर पर, यह जवाब एक सफाई का उपयोग कर सकता है - केवल फाइनल while( !(in>>ws).eof() )आम तौर पर मजबूत होता है, और इसे अंत में दफन किया जाता है।
टोनी डेलरो

74

क्योंकि यदि प्रोग्रामर लिखते नहीं हैं while(stream >> n), तो वे संभवतः यह लिखते हैं:

while(!stream.eof())
{
    stream >> n;
    //some work on n;
}

यहाँ समस्या यह है, आप some work on nपहली जाँच के बिना नहीं कर सकते हैं यदि स्ट्रीम सफल थी, क्योंकि यदि यह असफल रहा, तो आपका some work on nपरिणाम अवांछित होगा।

पूरे मुद्दा यह है कि, है eofbit, badbitया failbitसेट कर रहे हैं एक प्रयास के बाद स्ट्रीम से पढ़ने का बना है। इसलिए, यदि stream >> nविफल होता है, तो eofbit, badbitया failbitतुरंत सेट किया जाता है, इसलिए यदि आप लिखते हैं तो इसका अधिक मुहावरा है while (stream >> n), क्योंकि लौटी हुई वस्तु streamधर्मान्तरित होती है falseयदि स्ट्रीम से पढ़ने में कुछ विफलता थी और परिणामस्वरूप लूप बंद हो जाता है। और यह trueअगर धर्मान्तरित सफल था और लूप जारी रहता है।


1
के अपरिभाषित मूल्य पर काम करने के साथ उल्लिखित "अवांछित परिणाम" के अलावा n, प्रोग्राम भी एक अनंत लूप में गिर सकता है , अगर असफल स्ट्रीम ऑपरेशन किसी इनपुट का उपभोग नहीं करता है।
मस्तोव

10

अन्य उत्तरों ने समझाया है कि तर्क गलत क्यों है while (!stream.eof())और इसे कैसे ठीक किया जाए। मैं कुछ अलग करना चाहता हूं:

क्यों ईओफ़ के लिए जाँच स्पष्ट रूप से iostream::eofगलत का उपयोग कर रही है?

सामान्य शब्दों में, eof केवल>> फाइल की जाँच गलत है क्योंकि स्ट्रीम निष्कर्षण ( ) फ़ाइल के अंत को पकड़े बिना विफल हो सकता है। यदि आपके पास ईजी है int n; cin >> n;और स्ट्रीम शामिल है hello, तो hएक वैध अंक नहीं है, इसलिए इनपुट के अंत तक पहुंचने के बिना निष्कर्षण विफल हो जाएगा।

यह समस्या, इसे पढ़ने की कोशिश करने से पहले स्ट्रीम स्थिति की जांच करने के सामान्य तर्क त्रुटि के साथ संयुक्त है, जिसका अर्थ है एन इनपुट आइटम के लिए लूप एन + 1 बार चलेगा, निम्न लक्षणों की ओर जाता है:

  • यदि स्ट्रीम खाली है, तो लूप एक बार चलेगा। >>विफल हो जाएगा (पढ़ने के लिए कोई इनपुट नहीं है) और वे सभी चर जो सेट किए जाने चाहिए थे ( stream >> xवास्तव में) एकतरफा हैं। इससे कचरा डेटा संसाधित किया जा रहा है, जो निरर्थक परिणामों (अक्सर बड़ी संख्या) के रूप में प्रकट हो सकता है।

    (यदि आपका मानक पुस्तकालय C ++ 11 के अनुरूप है, तो चीजें अब थोड़ी भिन्न हैं: एक असफल >>अब संख्यात्मक चर सेट करने के 0बजाय उन्हें बिना अनुमति के छोड़ देता है (एस को छोड़कर char)।

  • यदि स्ट्रीम खाली नहीं है, तो अंतिम वैध इनपुट के बाद लूप फिर से चलेगा। चूंकि अंतिम पुनरावृत्ति में सभी >>ऑपरेशन विफल हो जाते हैं, चर पिछले पुनरावृत्ति से अपना मूल्य रखने की संभावना रखते हैं। यह "अंतिम पंक्ति दो बार मुद्रित होती है" या "अंतिम इनपुट रिकॉर्ड दो बार संसाधित होता है" के रूप में प्रकट हो सकता है।

    (यह C ++ 11 (ऊपर देखें) के बाद से थोड़ा अलग प्रकट होना चाहिए: अब आपको बार-बार अंतिम पंक्ति के बजाय शून्य का "प्रेत रिकॉर्ड" मिलता है।)

  • यदि स्ट्रीम में विकृत डेटा है, लेकिन आप केवल जांच करते हैं .eof, तो आप एक अनंत लूप के साथ समाप्त होते हैं। >>स्ट्रीम से किसी भी डेटा को निकालने में विफल हो जाएगा, इसलिए लूप कभी भी अंत तक पहुंचने के बिना घूमता है।


पुनरावृत्ति करने के लिए: समाधान >>स्वयं ऑपरेशन की सफलता का परीक्षण करना है, न कि एक अलग .eof()विधि का उपयोग करना :, while (stream >> n >> m) { ... }जैसे कि सी में आप scanfकॉल की सफलता का स्वयं परीक्षण करते हैं while (scanf("%d%d", &n, &m) == 2) { ... }:।


1
यह सबसे सटीक उत्तर है, हालाँकि c ++ 11 के रूप में, मुझे विश्वास नहीं हो रहा है कि चर अब (पहले गोली पीटी)
अनइंस्टाल्यूटेड हैं
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.