[컴퓨터공학]/[시스템 프로그래밍]

[시스템 프로그래밍] Low-level File IO(2) - Read, Write

딥러닝 도전기 2022. 9. 22. 14:36

[시스템 프로그래밍] Low-level File IO(2) - Read, Write


이전 포스팅에서 File Open and Close와 그에 필요한 Parameters를 알아보았습니다.

이번 포스팅에서는 파일 읽고 쓰기에 대해 작성하겠습니다.

 


1. 파일 읽기

$ man -s 2 read

파일 읽기와 관련된 함수인 read 입니다. 파라미터로는 fildes, *buf, nbyte가 있습니다.

fildes : file descriptor, 파일을 생성할 때 할당되는 file descriptor 입니다. 어떤 파일인지 알기 위한 인자(File ID)라고 생각하셔도 괜찮을 것으로 보입니다.
buf : buffer, 읽은 내용을 저장할 buffer의 시작 주소입니다.
nbyte : 파일에서 읽을 byte의 수 입니다.
Return : 실제로 읽은 byte의 수 (0 : 파일을 다 읽었다는 의미, -1 : 에러)

 

2. 파일 쓰기

$ man -s 2 write

 

파일 쓰기와 관련된 함수인 write입니다. 파라미터는 fildes, *buf, nbyte가 있습니다.

 

fildes : file descriptor, 기록하려는 파일의 file descriptor를 의미합니다.
buf : buffer, 기록할 내용이 저장된 buffer의 시작 주소입니다.
count : 기록할 byte수 입니다.
return : 기록한 byte수를 return합니다. 에러가 발생했다면 -1을 리턴합니다.

(buf 앞에 붙은 const는 write 함수 내에서 buf값을 변경하지 못하게 합니다.)

 


2. File Offset : 파일이 현재 가르키는 위치

File offset이란, 파일의 시작점부터 현재 위치까지의 byte 수 입니다. 파일이 byte 단위로 작성되기 때문에 이것은 파일이 현재 가르키는 위치와 동일합니다. offset을 기준으로 이어서 읽거나, 이어서 작성하는 작업이 진행됩니다.

 


3. Example

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main(void){
        int rfd, wfd, n;
        char buf[10];

        rfd = open("hello.txt", O_RDONLY);
        if(rfd == -1){
                perror("Open hello.txt");
                exit(1);
        }

        wfd = open("hello.bak", O_CREAT | O_WRONLY | O_TRUNC, 0644);
        if (wfd == -1){
                perror("Open  hello.bak");
                exit(1);
                }

        while((n=read(rfd, buf, 6)) > 0)
                if (write(wfd, buf, n) != n) perror("Write");
        if(n==-1) perror("Read");

        close(rfd);
        close(wfd);
        return 0;
}

위의 예시는 "hello.txt"에 있는 내용을 "hello.bak"이라는 파일로 복사하여 저장하는 코드입니다.

 

1) file descriptor, buffer 변수 선언

int rfd, wfd, n;
char buf[10];

우선 rfd(read file descriptor), wfd(write file descriptor)와 파일을 끝까지 읽었는지 확인하기 위한 변수인 n을 integer로 선언합니다. 또한, 읽어온 내용을 저장하기 위한 buf(buffer)를 char로 선언합니다.

 

 

2) "hello.txt" 파일 읽기 전용으로 열기

rfd = open("hello.txt", O_RDONLY);
if (rfd == -1){
	perror("Open hello.txt");
    exit(1);
}

open() 함수를 통하여 "hello.txt"파일을 읽기 전용 모드로 읽어옵니다.

open 함수는 integer 값인 file descriptor를 반환하므로 1)에서 정의한 rfd 변수에 저장합니다.

또한, open 함수에서 -1을 반환하면 에러가 발생했다는 의미기 때문에 perror()을 통해 에러를 알리고 exit(1);을 통해 종료합니다.

 

 

3) "hello.bak" 파일 쓰기 전용으로 열기(생성)

wfd = open("hello.bak", O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (wfd == -1){
	perror("Open  hello.bak");
	exit(1);
}

open() 함수와 O_CREAT oflag 를 통하여 "hello.bak" 을 생성합니다. oflag를 살펴보면 O_CREAT | O_WRONLY | O_TRUNC 인 것을 확인할 수 있습니다. 

다시한번 flags를 정리해보자면, 아래와 같습니다.

  • O_CREAT : 파일이 없으면 파일을 생성
  • O_TRUNC : O_CREAT와 함께 사용될 때, 파일이 있으면 내용을 지우고 파일을 생성
  • O_WRONLY : 파일을 쓰기 전용으로 열기

즉, "hello.bak"이라는 파일을 쓰기 전용으로 생성하면, 만약 이미 파일이 있다면 내용을 지웁니다.

동일하게, file descriptor가 -1을 반환했다면 에러가 발생했다는 것이니 오류처리를 해줍니다.

 

 

4) "hello.txt" 내용을 "hello.bak"에 옮기기

while((n=read(rfd, buf, 6)) > 0)
        if (write(wfd, buf, n) != n) perror("Write");
if(n==-1) perror("Read");

close(rfd);
close(wfd);

n = read(rfd, buf, 6) 을 먼저 살펴보겠습니다.

read()

rfd는 file descriptor, buf는 buffer, 6은 nbyte 입니다. nbyte는 한번에 몇 byte씩 읽을지를 결정하고, buffer은 읽을 내용을 저장할 buffer의 시작 주소입니다. 즉, file descriptor가 가르키는 파일을 6byte씩 읽어오며 버퍼에 저장합니다.

n은 읽어온 byte수를 의미하며 n이 0이면 모두 읽었다는 것이므로 while문을 종료하고, n이 -1이면 오류가 발생했다는 것이기 때문에 perror을 통해 오류를 출력합니다.

 

write()

wdf는 작성할 파일의 file descriptor, buffer는 읽어올 내용이 위치한 시작 주소, nbyte는 한번에 읽어올 byte 수 입니다.

while문 내부에 있는 if문에서 write(wfd, buf, n)은 wfd가 가르키는 파일에 buf 주소부터 n개 byte의 내용을 파일에 작성합니다. 리턴값은 한번에 작성한 byte의 수 이며, 그렇기 때문에 write(wfd, buf, n) != n 이면 오류를 발생시킵니다.

 

 

만약 총 문자열의 길이가 14byte면

첫 번째 while문 순회에서 n=6이고, wfd가 가르키는 파일("hello.bak")에 6byte를 저장합니다.

두 번째 while문 순회에서 또한 n=6이고, wfd가 가르키는 파일("hello.bak")에 6byte를 저장합니다.

세 번째 while문 순회에서는 n=2가 되며, wfd가 가르키는 파일("hello.bak")에 2byte를 저장합니다.

 

반응형