Windows

psycho 2012. 10. 1. 21:52

 POSIX 표준에는 pread()라는 함수가 있다. 이는 read()와 매우 유사하게 동작하는 함수인데, 다음과 같이 정의되어 있다.


SYNOPSIS1
       #include <unistd.h>

       ssize_t pread(int fildes, void *buf, size_t nbyte, off_t offset);
       ssize_t read(int fildes, void *buf, size_t nbyte);


 이 함수는 기능이 유사함에도 read()와의 차이점이 존재하는데, 한 가지는 offset 인자를 설정해서 임의의 위치에서 파일의 내용을 읽어올 수 있다는 것이고, 다른 한 가지는 읽기를 수행한 후에도 file pointer가 변하지 않는다는 것이다. 이 함수를 자체적으로 지원하는 운영체제에서는 상관없지만 그렇지 않은 운영체제의 대표격인 Windows에서 이 함수를 이용하려면 대체 구현을 하는 수밖에 없다. 그런데, 구글신(...)에게 물어본 결과 다음과 같은 구현2을 쉽게 찾을 수 있었다.

int pread(unsigned int fd, char *buf, size_t count, int offset)
{
if (_lseek(fd, offset, SEEK_SET) != offset) {
return -1;
}
return read(fd, buf, count);
}

 도대체 뭐가 문제인지 보이지 않는 분은 방금 위에서 설명한 read()와의 차이점을 다시 한 번 잘 읽어보기 바란다. 그렇다. 이 함수는 실행하고 나서도 변하지 않아야 할 file pointer가 변한다. 당연히 버그가 존재하는 잘못된 구현이라 할 수 있고, 이를 그대로 쓰는 것은 옳지 않을 것이다. 따라서 pread()를 굳이 쓰고 싶은 분이 있다면 다음 코드를 사용하라. SSIZE_T는 ssize_t의 Windows식 표현으로, 다른 운영체제에서는 POSIX에 정의된 ssize_t에 해당하는 type으로 바꿔주면 된다. _lseek()도 마찬가지로 바꿔주면 쓸 수 있다3.


SSIZE_T pread(int fildes, void *buf, SSIZE_T nbyte, off_t offset)

{

off_t pos = _lseek(fildes, 0, SEEK_CUR);

SSIZE_T nread;


if(pos == -1) return -1;


if(_lseek(fildes, offset, SEEK_SET) != offset) return -1;


// 여기서 오류가 발생하면 자동으로 -1을 돌려준다.

nread = read(fildes, buf, nbyte);


// 이 부분에서 오류가 날 것이 두려운 분은 오류 처리를 해 주면 된다.

_lseek(fildes, pos, SEEK_SET);

return nread;

}


※2012-12-12 15:23 수정사항 : file pointer 유지에 관한 버그 수정.

각주 1

http://www.cse.yorku.ca/tdb/_doc.php/userg/man/name/pread.3p/section/3p

각주 2

https://gist.github.com/1258986

각주 3

순전히 참고사항으로, POSIX 준수 운영체제라면 어지간하면 구현되어 있을 것이다. 자신이 사용하는 운영체제에서 대체 구현을 하지 않고도 쓸 수 있는지 먼저 조사하도록 하자.

  1. http://www.cse.yorku.ca/tdb/_doc.php/userg/man/name/pread.3p/section/3p [본문으로]
  2. https://gist.github.com/1258986 [본문으로]
  3. 순전히 참고사항으로, POSIX 준수 운영체제라면 어지간하면 구현되어 있을 것이다. 자신이 사용하는 운영체제에서 대체 구현을 하지 않고도 쓸 수 있는지 먼저 조사하도록 하자. [본문으로]