[c++] 루프 조건 (즉,`(while (! stream.eof ())`) 내부의 iostream :: 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() ) { /* ... */ }
eof 이후의 읽기 시도로 인한 실패는 종료 조건으로 간주됩니다. 이는 성공적인 스트림과 eof 이외의 이유로 실제로 실패한 스트림을 쉽게 구별 할 수있는 방법이 없음을 의미합니다. 다음 스트림을 취하십시오.
1 2 3 4 5<eof>
1 2 a 3 4 5<eof>
a<eof>
while(in>>data)
세 입력 모두에 대해 세트 failbit
로 종료됩니다 . 첫 번째와 세 번째 에도 설정됩니다. 따라서 루프를 지나면 적절한 입력 (1)과 부적절한 입력 (2 및 3)을 구별하기 위해 매우 추한 추가 논리가 필요합니다.eofbit
반면 다음을 수행하십시오.
while( !in.eof() )
{
int data;
in >> data;
if ( in.fail() ) /* handle with break or throw */;
// now use data
}
여기에서 in.fail()
읽을 것이있는 한 올바른 것이 맞는지 확인합니다. 목적은 단순한 while 루프 터미네이터가 아닙니다.
지금까지는 좋지만 스트림에 후행 공간이 있으면 어떻게 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
상기 설정 상태 공간 스트림 후행 잠재적 (0 개 이상) 스킵 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)
, 더 일반적이고 간결한 관용구이며, 간단한 (읽기 유형별 단일 데이터) 시나리오에서 선호 될 수 있습니다.
답변
프로그래머가 쓰지 않으면 다음과 같이 쓸 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
읽기에 성공하면 루프가 계속됩니다.
답변
다른 답변은 왜 논리가 틀렸고 while (!stream.eof())
어떻게 고치는지를 설명했습니다. 나는 다른 것에 집중하고 싶다 :
eof를 명시 적으로
iostream::eof
잘못 사용하는 이유는 무엇입니까?
일반적으로 스트림 추출 ( )이 파일 끝을 누르지 않고 실패 할 수 있기 때문에 점검 eof
만 잘못되었습니다 >>
. 당신은 예를 들어이있는 경우 int n; cin >> n;
와 스트림에 포함 된 hello
후, h
추출, 입력의 끝에 도달없이 실패 할 것이다, 그래서 유효한 숫자가 아닙니다.
이 문제 는 읽기 전에 스트림 상태를 확인하는 일반적인 논리 오류와 결합하여 N 개의 입력 항목에 대해 루프가 N + 1 회 실행됨을 의미하여 다음과 같은 증상을 유발합니다.
-
스트림이 비어 있으면 루프가 한 번 실행됩니다.
>>
에 실패하고 (읽을 입력이 없음) 설정 한 모든 변수stream >> x
가 실제로 초기화되지 않았습니다 . 이로 인해 가비지 데이터가 처리되고, 이는 무의미한 결과 (종종 큰 숫자)로 나타날 수 있습니다.(표준 라이브러리가 C ++ 11을 준수하는 경우 이제 상황이 조금 다릅니다. 실패
>>
하면 숫자 변수를0
초기화되지 않은 상태로 설정합니다 (char
s 제외 ).) -
스트림이 비어 있지 않으면, 마지막 유효한 입력 후에 루프가 다시 실행됩니다. 마지막 반복에서 모든
>>
작업이 실패 하므로 변수는 이전 반복에서 값을 유지합니다. “마지막 줄이 두 번 인쇄됩니다”또는 “마지막 입력 레코드가 두 번 처리됩니다”로 나타날 수 있습니다.(이것은 C ++ 11부터 조금 다르게 나타납니다 (위 참조) : 이제 반복되는 마지막 줄 대신 “0”의 “팬텀 레코드”가 나타납니다.
-
스트림에 잘못된 형식의 데이터가 포함되어 있지만 확인
.eof
만하면 무한 루프가 발생합니다.>>
스트림에서 데이터를 추출하지 못하므로 루프는 끝까지 도달하지 않고 제자리에서 회전합니다.
요약하자면, 해결책은 C와 마찬가지로 호출 자체 의 성공을 테스트하는 것처럼 >>
별도의 .eof()
메소드 를 사용하지 않고 작업 자체 while (stream >> n >> m) { ... }
의 성공을 테스트하는 scanf
것 while (scanf("%d%d", &n, &m) == 2) { ... }
입니다.