본문 바로가기
Computer Science/UNIX & Linux

[UNIX/Linux] ep3+) 저수준 파일 입출력 함수 실습

by 클레어몬트 2024. 10. 2.

ex1. 저수준 파일 입출력을 이용해 파일을 복사하는 프로그램을 작성하시오. 이때 파일명은 명령행 인자로 받는다.

./ex test.txt test.bak

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

int main(int argc, char* argv[]) {
    if (argc != 3) {
        printf("Usage: ex1 src target\n");
        exit(1);
    }

    int rfd, wfd, n;
    char buf[256];
 
    rfd = open(argv[1], O_RDONLY);
    if (rfd == -1) {
        perror("Open ");
        exit(1);
    }

    wfd = open(argv[2], O_CREAT | O_WRONLY | O_TRUNC, 0644);
    if (wfd == -1) {
        perror("Create ");
        exit(1);
    }

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

    return 0;
}

test.bak 파일이 새로 생긴 모습

 

 

 

ex2. 저수준 파일 입출력을 이용해 현재 디렉터리 아래에 새로운 디렉터리를 생성하고 파일을 이동시키는 프로그램을 작성하시오. 이때 생성할 디렉터리명과 파일명은 명령행 인자로 받는다.

./ex testdir test.txt

 

strcpy(fname, argv[1]);

strcat(fname, "/");

strcat(fname, argv[2]);

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>

int main(int argc, char* argv[]) {
    if (argc != 3) {
        printf("Usage: ex2 dir file\n");
        exit(1);
    }

    int rfd, wfd, n;
    char buf[256];
    char fname[256];

    mkdir(argv[1], 0755);

    rfd = open(argv[2], O_RDONLY);
    if (rfd == -1) {
        perror("Open ");
        exit(1);
    }

    strcpy(fname, argv[1]);
    strcat(fname, "/");
    strcat(fname, argv[2]);

    wfd = open(fname, O_CREAT | O_WRONLY | O_TRUNC, 0644);
    if (wfd == -1) {
        perror("Create ");
        exit(1);
    }

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

    close(rfd);
    close(wfd);
    remove(argv[2]);

    return 0;
}

testdir이 새로 생기고, 그 안으로 test.txt 파일이 이동한 모습

 

 

 

ex3. lseek() 함수를 이용해 다음 데이터 파일을 읽어 출력 형태와 같이 출력하는 프로그램을 작성하시오

데이터파일 : abcdefghijklmnopqrstuvwxyz

출력형태 : acegikmoqsuwy

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>

int main() {
    int fd, n; 
    off_t start, cur;
    char buf[256];
 
    fd = open("test.dat", O_RDONLY);
    if (fd == -1) {
        perror("Open test.dat");
        exit(1);
    }

    start = lseek(fd, 0, SEEK_CUR);

    while ((n = read(fd, buf, 1)) > 0) {
        write(1, buf, 1);
        start = lseek(fd, 1, SEEK_CUR);
    }
    close(fd);

    return 0;
}

test.dat 파일에서 문제의 요구대로 출력한 모습(파일의 내용이 바뀌지는 않는다)

 

 

 

ex4. 명령행 인자가 있는지 조사하고, 만일 인자가 존재하면, 각 인자를 하나의 파일 이름으로 취급하고 각 파일의 내용을 표준 출력으로 복사하는 프로그램을 작성하시오. 만일 명령행 인자가 존재하지 않으면, 입력을 표준 입력으로부터 받아야 한다.

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

#define SIZE 1024

int main(int argc, char* argv[]) {
    ssize_t nread;
    char buf[SIZE];

    if (argc == 1) {
        while ((nread = read(0, buf, SIZE)) > 0) {
            write (1, buf, nread);
        }
        exit(0);
    }

    int fd, i;
    for (i = 1; i < argc; i++) {
        fd = open(argv[i], O_RDWR, 0644);

        memset(buf, 0, SIZE);
        while ((nread = read(fd, buf, SIZE)) > 0) {
            write(1, buf, SIZE);
        }
        close(fd);
    }

    return 0;
}

 

 

 

ex5. test3.c 파일을 수정하여 현재의 파일 플래그를 테스트하여 출력하고, O_APPEND를 추가하는 프로그램을 작성하라

 

switch (현재 파일플래그 & O_ACCMODE) {

    case O_RDWR :

    …

}

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>

int main(void) {
    int fd, flags;

    //fd = open("test.txt", O_RDWR);
    //fd = open("test.txt", O_RDONLY);
    fd = open("test.txt", O_WRONLY);
    if (fd == -1) {
        perror("open error (fcntl)");
        exit(1);
    }

    if ((flags = fcntl(fd, F_GETFL)) == -1) {
        perror("fcntl");
        exit(1);
    }

    flags |= O_APPEND;

    if (fcntl(fd, F_SETFL, flags) == -1) {
        perror("fcntl");
        exit(1);
    }

    if (write(fd, "UNIX Programming Class", 32) != 32) {
        perror("write");
    }
    close(fd);

    return 0;
}

 

↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

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

int main(void) {
	int fd;
	int arg1;

	fd = open("testdata1", O_WRONLY|O_CREAT,0644);

	write(fd, "hi there...\n", 15);

	lseek(fd, (off_t)0, SEEK_SET);

	fcntl(fd, F_SETFL, O_WRONLY|O_APPEND);

	write (fd, "students...\n", 15);

	arg1 = fcntl(fd, F_GETFL);
	switch (arg1 & O_ACCMODE) {
		case O_WRONLY:
			printf("write-only\n");
			break;
		case O_RDWR:
			printf("read_write\n");
			break;
		case O_RDONLY:
			printf("read-only\n");
			break;
		default:
			printf("No such mode");
	}

	if (arg1 & O_APPEND) {
		printf(" - append flag set\n");
	}
	close (fd);

	return 0;
}

 

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ

 

createdata.c 파일

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

char* name[] = {
    "David Hutchison",
    "Takeo Kanade",
    "Josef Kittler",
    "John M. Kleinberg",
    "Friedemann Mattern",
    "John C. Mitchell",
    "Moni Naor",
    "Oscar Nierstrasz",
    "C. Pandu Rangan",
    "Bernhard Steffern",
    "Madhu Sudan",
    "Demetri Tygar",
    NULL
};

int main() {
    int i, out;
    char wbuf[64];

    memset(wbuf, 0, sizeof(wbuf));

    if ((out = open("residents", O_CREAT|O_RDWR, 0644)) < 0) {
        perror("no testdata");
        exit(1);
    }

    for (i = 0; name[i] != NULL; i++) {
        memcpy(wbuf, name[i], sizeof(wbuf));
        write(out, wbuf, sizeof(wbuf));
        printf(" %s %d %s\n", wbuf, i+1, name[i]);
        memset(wbuf, 0, sizeof(wbuf));
    }
    close(out);
    
    return 0;
}

 

residents 파일 생성

 

 

ex1. residents 파일을 입력받아 위치를 64문자씩 옮기며 출력

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

#define NAMELENGTH 64

char namebuf[NAMELENGTH];
int infile = -1;

int main(void) {
    off_t offset;
    ssize_t nread;
    int i = 0;

    if (infile == -1 && (infile = open("residents", O_RDONLY)) == -1) {
        exit(1);
    }

    for (i = 0; i < 12; i++) {
        offset = (i) * NAMELENGTH;

        if (lseek(infile, offset, SEEK_SET) == -1) {
            exit(1);
        }

        if ((nread = read(infile, namebuf, sizeof(namebuf))) <= 0) {
            perror("Read error");
            exit(1);
        }

        printf("room = %d, %s\n", i + 1, namebuf);
    }

    return 0;
}

 

 

 

ex2. 호텔에 투숙한 사람들의 이름을 기록한 residents라는 파일이 있다고 가정한다. 첫째 줄은 1호실에 투숙한 사람의 이름, 둘째 줄은 2호실에 투숙한 사람의 이름 등으로 구성되어 있다. 이름은 64개의 문자로 되어 있다. 방 번호가 명령행 인자로 주어지면 투숙객의 이름을 출력하는 프로그램을 작성할 것

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

#define NAMELENGTH 64

char namebuf[NAMELENGTH];
int infile = -1;

int main(int argc, char* argv[]) {
    off_t offset;
    ssize_t nread;
    int i = 0;

    if (infile == -1 && (infile = open("residents", O_RDONLY)) == -1) {
        exit(1);
    }

    offset = (atoi(argv[1]) - 1) * NAMELENGTH;
    if (lseek(infile, offset, SEEK_SET) == -1) {
        perror("Seek error");
        exit(1);
    }

    if ((nread = read(infile, namebuf, sizeof(namebuf))) <= 0) {
        perror("Read error");
        exit(1);
    }    
    printf("room = %d, %s\n", atoi(argv[1]), namebuf);
	
    close(infile);

    return 0;
}

 

 

 

ex3. 1K의 integer를 생성한 후, 정수형 임의의 파일 시작 위치길이를 명령행 인자로 받아 그 위치로 이동한 후, 입력 길이만큼 출력하는 프로그램을 작성할 것

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

#define WBUFSIZE 1024 
#define RBUFSIZE 64 

int main(int argc, char* argv[]) {
    int fd, i, n;
    int wbuf[WBUFSIZE], rbuf[RBUFSIZE];
	int offset, length;

	fd = open("testdata", O_RDWR|O_CREAT, 0644);
	for (i = 0; i < 1024; i++) {
		wbuf[i] = i;
	}
	write(fd, wbuf, sizeof(wbuf));
	close(fd);

	offset = atoi(argv[1]);
	offset *= sizeof(int);

	length = atoi(argv[2]);

	fd = open("testdata", O_RDWR, 0644);
	bzero(rbuf, sizeof(rbuf));

	lseek(fd, (off_t)offset, SEEK_SET);
	read(fd, rbuf, length*sizeof(int));

	for (i = 0; i < length; i++) {
		printf("%d ", rbuf[i]);
	}
	printf("\n");
	close(fd);

	return 0;
}

 

 

 

hw. 두 방 번호를 command-line으로 입력받아, 두 방의 투숙객을 교환한다. 그 후 모든 방 투숙객을 출력하여 제대로 수정되었는지를 확인할 것

./hw 3 5 (3번 방과 5번 방 투숙객을 교환, 방 번호는 1번부터 시작)

    (customer list before room change)

    -----

    (customer list after room change)

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

#define RECORD_SIZE 64 // 각 투숙객 이름의 최대 크기

int main(int argc, char* argv[]) {
    if (argc != 3) {
        fprintf(stderr, "Usage: %s <room1> <room2>\n", argv[0]);
        exit(1);
    }

    int room1 = atoi(argv[1]);
    int room2 = atoi(argv[2]);
    char buf1[RECORD_SIZE], buf2[RECORD_SIZE];
	
    // 파일 열기
    int fd = open("residents", O_RDWR);
    
    // 방 교환 전 투숙객 리스트 출력
    printf("[customer list before room change]\n");
    lseek(fd, 0, SEEK_SET); // 파일의 처음으로 이동
    char buf[RECORD_SIZE];
    int i;
    for (i = 0; i < 12; i++) {
        read(fd, buf, RECORD_SIZE);
        printf("Room %d: %s\n", i + 1, buf); // 방 번호는 1부터 시작
    }
    
    // room1의 투숙객 정보 읽기
    lseek(fd, (room1 - 1) * RECORD_SIZE, SEEK_SET); // room1 위치로 이동
    read(fd, buf1, RECORD_SIZE);

    // room2의 투숙객 정보 읽기
    lseek(fd, (room2 - 1) * RECORD_SIZE, SEEK_SET); // room2 위치로 이동
    read(fd, buf2, RECORD_SIZE);

    // room1과 room2의 투숙객 정보 교환
    lseek(fd, (room1 - 1) * RECORD_SIZE, SEEK_SET); // room1 위치로 이동
    write(fd, buf2, RECORD_SIZE);
    lseek(fd, (room2 - 1) * RECORD_SIZE, SEEK_SET); // room2 위치로 이동
    write(fd, buf1, RECORD_SIZE);

    // 방 교환 후 투숙객 리스트 출력
    printf("\n[customer list after room change]\n");
    lseek(fd, 0, SEEK_SET); // 파일의 처음으로 이동
    for (i = 0; i < 12; i++) {
        read(fd, buf, RECORD_SIZE);
        printf("Room %d: %s\n", i + 1, buf); // 방 번호는 1부터 시작
    }

    close(fd);
    return 0;
}

3번 방과 5번 방의 투숙객이 바뀐 것을 확인할 수 있다

 

 

 

 

 

 

참고 및 출처: 시스템 프로그래밍 리눅스&유닉스(이종원)