[c++] 루프 조건 (즉,`(while (! stream.eof ())`) 내부의 iostream :: eof가 왜 잘못된 것으로 간주됩니까?

루프 응답에서 사용 하는 것이 “거의 확실하다”는 답변 의 주석을 찾았습니다 iostream::eof. 나는 일반적으로 while(cin>>n)EOF를 암시 적으로 확인하는 것 같은 것을 사용합니다 .

eof를 명시 적으로 while (!cin.eof())잘못 검사하는 이유는 무엇 입니까?

scanf("...",...)!=EOFC 에서 사용하는 것과 어떻게 다른 가요?



답변

스트림의 끝을 읽은 후에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초기화되지 않은 상태로 설정합니다 ( chars 제외 ).)

  • 스트림이 비어 있지 않으면, 마지막 유효한 입력 후에 루프가 다시 실행됩니다. 마지막 반복에서 모든 >>작업이 실패 하므로 변수는 이전 반복에서 값을 유지합니다. “마지막 줄이 두 번 인쇄됩니다”또는 “마지막 입력 레코드가 두 번 처리됩니다”로 나타날 수 있습니다.

    (이것은 C ++ 11부터 조금 다르게 나타납니다 (위 참조) : 이제 반복되는 마지막 줄 대신 “0”의 “팬텀 레코드”가 나타납니다.

  • 스트림에 잘못된 형식의 데이터가 포함되어 있지만 확인 .eof만하면 무한 루프가 발생합니다. >>스트림에서 데이터를 추출하지 못하므로 루프는 끝까지 도달하지 않고 제자리에서 회전합니다.


요약하자면, 해결책은 C와 마찬가지로 호출 자체 의 성공을 테스트하는 것처럼 >>별도의 .eof()메소드 를 사용하지 않고 작업 자체 while (stream >> n >> m) { ... }의 성공을 테스트하는 scanfwhile (scanf("%d%d", &n, &m) == 2) { ... }입니다.


답변