iostream :: eof가 루프 조건 (예 :`while (! stream.eof ())`) 내부에서 잘못된 것으로 간주되는 이유는 무엇입니까?
이 답변 iostream::eof
에서 루프 조건에서 사용 하는 것이 "거의 확실히 잘못되었습니다" 라는 주석을 찾았습니다 . 나는 일반적으로 while(cin>>n)
EOF를 암시 적으로 확인하는 것과 같은 것을 사용합니다 .
eof를 명시 적으로 while (!cin.eof())
잘못 사용하는 이유는 무엇 입니까?
scanf("...",...)!=EOF
C 에서 사용하는 것과 어떻게 다른 가요?
왜냐하면 스트림의 끝을 읽은 후에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
결론 상단 : 공백을 적절히 처리하면 다음과 같은 방법을 eof
사용할 수 있습니다 ( fail()
오류 검사 보다 더 신뢰할 수 있음 ).
while( !(in>>std::ws).eof() ) {
int data;
in >> data;
if ( in.fail() ) /* handle with break or throw */;
// now use data
}
( 답을 강조하기위한 제안을 해주신 Tony D에게 감사드립니다. 이것이 왜 더 강력한 지에 대한 예는 아래 그의 의견을 참조하십시오. )
사용에 대한 주된 주장 eof()
은 공백의 역할에 대한 중요한 미묘함이 누락 된 것 같습니다. 내 제안은 eof()
명시 적으로 확인 하는 것이 " 항상 잘못됨 " 일뿐만 아니라 ( 이와 유사한 SO 스레드에서 가장 중요한 의견 인 것 같음)뿐만 아니라 공백을 적절하게 처리하면 더 깨끗하고 신뢰할 수 있다는 것입니다. 오류 처리이며 항상 올바른 솔루션입니다 (가장 간결한 것은 아니지만).
"적절한"종료 및 읽기 순서로 제안되는 내용을 요약하면 다음과 같습니다.
int data;
while(in >> data) { /* ... */ }
// which is equivalent to
while( !(in >> data).fail() ) { /* ... */ }
The failure due to read attempt beyond eof is taken as the termination condition. This means is that there is no easy way to distinguish between a successful stream and one that really fails for reasons other than eof. Take the following streams:
1 2 3 4 5<eof>
1 2 a 3 4 5<eof>
a<eof>
while(in>>data)
terminates with a set failbit
for all three input. In the first and third, eofbit
is also set. So past the loop one needs very ugly extra logic to distinguish a proper input (1st) from improper ones (2nd and 3rd).
Whereas, take the following:
while( !in.eof() )
{
int data;
in >> data;
if ( in.fail() ) /* handle with break or throw */;
// now use data
}
Here, in.fail()
verifies that as long as there is something to read, it is the correct one. It's purpose is not a mere while-loop terminator.
So far so good, but what happens if there is trailing space in the stream -- what sounds like the major concern against eof()
as terminator?
We don't need to surrender our error handling; just eat up the white-space:
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
skips any potential (zero or more) trailing space in the stream while setting the eofbit
, and not the failbit
. So, in.fail()
works as expected, as long as there is at least one data to read. If all-blank streams are also acceptable, then the correct form is:
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
}
Summary: A properly constructed while(!eof)
is not only possible and not wrong, but allows data to be localized within scope, and provides a cleaner separation of error checking from business as usual. That being said, while(!fail)
is inarguably a more common and terse idiom, and may be preferred in simple (single data per read type of) scenarios.
Because if programmers don't write while(stream >> n)
, they possibly write this:
while(!stream.eof())
{
stream >> n;
//some work on n;
}
Here the problem is, you cannot do some work on n
without first checking if the stream read was successful, because if it was unsuccessful, your some work on n
would produce undesired result.
The whole point is that, eofbit
, badbit
, or failbit
are set after an attempt is made to read from the stream. So if stream >> n
fails, then eofbit
, badbit
, or failbit
is set immediately, so its more idiomatic if you write while (stream >> n)
, because the returned object stream
converts to false
if there was some failure in reading from the stream and consequently the loop stops. And it converts to true
if the read was successful and the loop continues.
The other answers have explained why the logic is wrong in while (!stream.eof())
and how to fix it. I want to focus on something different:
why is checking for eof explicitly using
iostream::eof
wrong?
In general terms, checking for eof
only is wrong because stream extraction (>>
) can fail without hitting the end of the file. If you have e.g. int n; cin >> n;
and the stream contains hello
, then h
is not a valid digit, so extraction will fail without reaching the end of the input.
This issue, combined with the general logic error of checking the stream state before attempting to read from it, which means for N input items the loop will run N+1 times, leads to the following symptoms:
If the stream is empty, the loop will run once.
>>
will fail (there is no input to be read) and all variables that were supposed to be set (bystream >> x
) are actually uninitialized. This leads to garbage data being processed, which can manifest as nonsensical results (often huge numbers).(If your standard library conforms to C++11, things are a bit different now: A failed
>>
now sets numeric variables to0
instead of leaving them uninitialized (except forchar
s).)If the stream is not empty, the loop will run again after the last valid input. Since in the last iteration all
>>
operations fail, variables are likely to keep their value from the previous iteration. This can manifest as "the last line is printed twice" or "the last input record is processed twice".(This should manifest a bit differently since C++11 (see above): Now you get a "phantom record" of zeroes instead of a repeated last line.)
If the stream contains malformed data but you only check for
.eof
, you end up with an infinite loop.>>
will fail to extract any data from the stream, so the loop spins in place without ever reaching the end.
To recap: The solution is to test the success of the >>
operation itself, not to use a separate .eof()
method: while (stream >> n >> m) { ... }
, just as in C you test the success of the scanf
call itself: while (scanf("%d%d", &n, &m) == 2) { ... }
.
'Nice programing' 카테고리의 다른 글
동일한 디렉토리 또는 하위 디렉토리 내에서 클래스를 가져 오는 방법은 무엇입니까? (0) | 2020.10.03 |
---|---|
파이썬 모듈의 버전을 확인하는 방법은 무엇입니까? (0) | 2020.10.03 |
ELMAH가 ASP.NET MVC [HandleError] 특성과 함께 작동하도록하는 방법은 무엇입니까? (0) | 2020.10.03 |
파이썬에서 단일 밑줄“_”변수의 목적은 무엇입니까? (0) | 2020.10.03 |
자바 스크립트 '바인딩'방법의 사용은 무엇입니까? (0) | 2020.10.03 |