Studies

C언어의 feof() 함수 사용에 대해

로볼키 2015. 3. 22. 20:51

Studies 카테고리는 제가 공부하다가 기록하는 용도로 씁니다.

혹시 여기에 기록된 것이 잘못되었다면, 댓글로 꼭 알려주세요.




feof, file - end of file

이라고 해서 파일의 끝에 도달하면 멈춘다고 생각하기 쉽습니다.


는 저도 지금 이거로 몇 시간; 날려서 쓰는 포스트.


결론부터 말하자면, C언어에서 feof()는 파일 포인터가 파일의 끝에 도달했을 때 끝나는게 아닙니다.



제가 쓰던 소스를 조금 넣어서 볼까요.

프로그램은 buffer 배열에 input.txt 에 있는 숫자들을 입력받는 단계만 떼어놨습니다..

최종 출력파일에는 오름차순 정렬하고 끝나고요.

입력받는 파일 포인터 fp입니다.


입력파일 형식은

2 40 43 12 6 55 


이런 식으로 띄어쓰기로 구분된 숫자들이구요.

        while(!feof(fp)) {
                        fscanf(fp, "%d", &buffer[check]);
                        printf("%d 번째 scanf 숫자: %d \n", check, buffer[check]);
                        check++;               
                }
        }


'파일이 끝날 때 까지' while 문을 돌린다.

돌리는 것: buffer 배열에 fscanf로 숫자를 입력하고,

몇 번째 숫자가 스캔되는지 프롬프트에 띄우고,

다음 배열에 입력을 하기 위해 check를 1 증가시킨다.

 

하지만 이렇게 만든 경우, 마지막까지 파일을 읽었음에도 불구하고, 제가 원하는 값인

2 40 43 12 6 55

가 나오지 않고,

0 2 40 43 12 6 55

이 나와버립니다.


printf 찍은 결과는


0 번째 scanf 숫자: 2
1 번째 scanf 숫자: 40
2 번째 scanf 숫자: 43
3 번째 scanf 숫자: 12
4 번째 scanf 숫자: 6
5 번째 scanf 숫자: 55
6 번째 scanf 숫자: 0


이렇게 마지막에 0이 들어가버리는거죠.



대체 이 문제가 뭘까,


이것 저것 바꿔보고 여기저기에 printf 찍어봐도 도저히 저의 머릿속에서 해답을 찾을 수가 없어서,

feof가 안 끝나는 현상에 대해 구글링을 했더니 바로 뙇...


그런데 몇몇 블로그에 있는 해결법은 적용이 안 되더군요.



제대로 된 설명을 보죠.


http://www.drpaulcarter.com/cs/common-c-errors.php#4.2

여기에 있는데요, 설명 부분만 간단하게 번역하고 나머지는 링크 가서 보시면 되겠습니다.


There is a wide spread misunderstanding of how C's feof() function works. Many programmers use it like Pascal's eof() function. However, C's function works differently!

What's the difference? Pascal's function returns true if the next read will fail because of end of file. C's function returns true if the last function failed.


많은 프로그래머들이 C의 feof() 함수를 Pascal의 eof()처럼 사용하지만, C의 feof()는 파스칼의 그것과 같은 방식으로 작동하지 않습니다. 차이점이 뭘까요?

파스칼의 eof는 '다음'에 읽어오는 부분이 파일의 끝이라 실패하는 경우에 true를 리턴합니다.

반면 C의 feof는 '마지막' 함수가 실패하는 경우에 true를 리턴합니다.


(즉, 파일 포인터가 끝을 가리켜도 함수는 한 번 더 실행이 되고, 거기서 에러가 나와야 true가 되어서 while을 빠져 나온다는 말.)


그래서 제안하는 방법.

while 반복문의 조건으로 feof()를 쓸 것이 아니라, 파일 포인터를 읽는 부분 바로 아래에 feof()를 체크하도록 합니다.


링크의 글을 읽다 보면, feof를 쓸 이유가 없다며 fgets의 예를 보여주는데...

제가 받는 값은 string도 character(fgetc)도 아닌 숫자라서,

제가 쓰던 fscanf를 변형하진 않고, 위에 나온 방법을 썼습니다.


적용한 코드는 아래에.


 while(1) {
                fscanf(fp, "%d", &buffer[check]);

                if(feof(fp)) break;

                printf("%d 번째 scanf 숫자: %d \n", check, buffer[check]);
                check++;

        }


while 조건은 별다른 것 없이 1 주고,

중간에

 if(feof(fp)) break;

를 넣습니다.


혹시나 해서 덧붙이자면, break;은 if에는 영향을 주지 않습니다. while이나 for에 영향을 주어 반복문을 멈추는 역할.


그래서 실행하면 프롬프트에는


0 번째 scanf 숫자: 2
1 번째 scanf 숫자: 40
2 번째 scanf 숫자: 43
3 번째 scanf 숫자: 12
4 번째 scanf 숫자: 6
5 번째 scanf 숫자: 55

여기서 멈추고,

정렬 후 출력한 결과도 

2 6 12 40 43 55

로 0 없이 잘 나옵니다.








728x90