Low-level vs High-level file IO
- Low-Level File IO (System call)
- 시스템 콜을 이용해서 파일 입출력 수행
- File Descriptor 사용
- Byte 단위로 디스크에 입출력
- 특수 파일에 대한 입출력 가능
- High-Level File IO (Buffered IO)
- C Standard library를 사용해서 파일 입출력 수행
- File Pointer 사용
- 버퍼(block) 단위로 디스크에 입출력 (여러 형식의 입출력 지원)
3강-1. 사용자 버퍼 IO
Disk address
- Physical disk address
- Sector(물리적 데이터 전송 단위)를 지정
- [Cylinder #, Surface #, Sector #]
- Logical disk address: relative address
- Disk system의 데이터 전체를 block들의 나열로 취급
- Block에 번호 부여
- 임의의 Block에 접근 가능
- Block 번호 -> physical address 모듈 필요 (disk driver)
- Disk system의 데이터 전체를 block들의 나열로 취급
- Disk address mapping이라는 작업을 통해, Logical disk address -> Physical disk address
Block
- 운영체제 입장에서 file system은 block들의 나열
- 파일 시스템의 추상화
- Disk 접근의 최소 단위
File IO vs Disk IO
- File IO with system call, Access in a unit of byte
- Disk IO, Access in a unit of block
- byte -> Buffer (Page write-back) -> block
Kernel buffer (Page cache)
- Page write-back 시점
- (커널의) 메모리 공간이 부족할 때
- 내용이 변경 된 후, 일정 시간이 지나면
fsync
호출 (강제 동기화)
- Write-back 시점은 유저 입장에서 예측하기 어려움
User-buffered IO
- OS에 맡기는 것이 아니라, 직접 버퍼를 만들어 사용하면 효율적으로 사용할 수 있음
3강-2. File open & close
Standard IO
- A platform-independent, user-buffering solution
- File pointer
- File operation을 관리하는 구조체(FILE)를 가리키는 포인터
- 내부적으로 file descriptor와 연동(mapping) 됨
- Stream
- 프로그램과 file을 연결하는 통로
- Stream of bytes
Workflow of file I/O
- 파일 열기
- 파일 스트림 생성 및 FILE 구조체에 저장
fopen()
- 파일 사용
- 파일의 내용 읽기 또는 정보 기록
fprintf(), fscanf(), fgetc(), fputc(), ...
- 파일 닫기
- 파일에 대한 스트렘 해제
Opening a file/stream
1
2
3
#include <stdio.h>
FILE *fopen(const char *path, const char *mode);
- path (file path): 열려는 파일의 경로
- mode (file open mode): 파일 열기 모드
- Return: file pointer, NULL: fail to open
파일 열기 모드(mode)
모드 | 파일 X | 파일 O | |
r | 읽기(read) | 열기 실패 | 열기 성공 |
w | 쓰기(write) | 파일 생성 | 덮어 쓰기 |
a | 덧붙이기(append) | 파일 생성 | 기존 파일의 뒤에 데이터를 기록 |
r+ | 읽기+ 모드 / 쓰기 모드로 전환 가능 | 전환시, fflush(), fseek(), fstepos(), rewind() 중 하나를 호출 | |
w+ | 쓰기+ 모드 / 읽기 모드로 전환 가능 | 전환시, fflush(), fseek(), fstepos(), rewind() 중 하나를 호출 | |
a+ | 덧붙이기+ / 읽기 모드로 전환 가능 | 전환시, fflush(), fseek(), fstepos(), rewind() 중 하나를 호출 | |
b | 이진(binary) 파일 모드 | 이진 파일 형식으로 파일 오픈 읽기/쓰기 모드는 위의 지정자들로 지정 (rb, wb, …) |
Ascii(test) file & Binary file
- Ascii (text) file (텍스트 파일)
- 문자들이 들어 있는 표시 -> 사람이 읽을 수 있는 형태
- 연속적인 줄(line)로 구성
- 각 문자는 ascii code로 표현 됨
- Binary file (이진 파일)
- 이진 데이터가 직접 저장 -> 사람이 그대로 읽기 어려운 형태
- 줄(line)로 구분되지 않는, 이진 데이터의 연속
- 컴퓨터가 읽을 수 있는 형태 -> 데이터를 효율적으로 다룰 수 있음
[EX] File open & colse by Standard IO
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
#include <stdlib.h>
int main(void){
FILE *fp;
if ((fp=fopen("hello.txt", "w"))==NULL) // file open
{
perror("fopen: hello.txt");
exit(1);
}
fclose(fp); // file close
return 0;
}
3강-3~4. File read & write
Character-based reading / writing
1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
// reading
int fget(FILE *stream); // 함수 형태로 구현
int getc(FILE *stream); // macro 형태로 구현 (인자에 수식 X)
int getchar(void); // = getc(stdin)
// writing
int fputc(int c, FILE *stream);
int putc(int c, FILE *stream);
int putchar(int c); // = putc(c, stdout)
- stream: File operation을 수행할 stream
- c (char): 쓰려는 문자
Return: 읽은/기록한 문자 EOF(-1): error
[EX] Character-based IO
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 문자 단위로 파일을 복사하는 프로그램
#include <stdlib.h>
#include <stdio.h>
int main(void){
FILE *rfp, *wfp;
int c;
if((rfp=fopen("hello.txt", "r"))==NULL){
perror("fopen: hello.txt");
exit(1);
}
if((wfp=fopen("hello.out", "w"))==NULL){
perror("fopen: hello.out");
exit(1);
}
// 파일 복사
while((c=fgetc(rfp))!=EOF){
fputc(c, wfp);
}
fclose(rfp);
fclose(wfp);
return 0;
}
String-based reading / writing
1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
// reading
char *gets(char *s); // get from stdin (buffer size에 대한 고려 X -> 보안문제)
char *fgets(char *s, int n, FILE *stream);
// stream에서 (n-1)개의 문자를 읽어서 s에 저장
// \n 또는 EOF를 만나면 해당 지점까지만 읽어 옴
// writing
int puts(char *s); // put to stdout (with \n)
int fputs(char *s, FILE *stream); // s를 stream에 출력 (\n 추가하지 않음)
- s(string): 읽은 문자열을 저장할 buffer
- n: buffer의 크기
- stream: File operation을 수행할 stream
Return: Buffer의 시작 주소 NULL: 읽을 것이 없음 (read) - Return: (write)
- 양수: sucess
- 음수: error
[EX] String-based IO
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 버퍼 단위로 파일을 복사하는 프로그램
#include <stdlib.h>
#include <stdio.h>
int main(void){
FILE *rfp, *wfp;
char buf[BUFSIZ]; // BUFSIZ is defined in the stdio.h
printf("BUFSIZ = %d\n", BUFSIZ);
if((rfp=fopen("hello.txt", "r"))==NULL){
perror("fopen: hello.txt");
exit(1);
}
if((wfp=fopen("hello.out", "a"))==NULL){
perror("fopen: hello.out");
exit(1);
}
// 파일 복사
while(fgets(buf, BUFSIZ, rfp)!=NULL){
fputs(buf, wfp);
}
fclose(rfp);
fclose(wfp);
return 0;
}
Binary IO
1
FILE *fopen(const char *name, const char mode);
rb | 읽기(read) / 이진파일(binary) |
wb | 쓰기(write) / 이진파일(binary) |
ab | 덧붙이기(append) / 이진파일(binary) |
rb+ | 읽기 확장(read extended) / 이진파일(binary) |
wb+ | 쓰기 확장(write extended) / 이진파일(binary) |
1
2
3
4
5
6
#include <stdio.h>
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
// 크기가 size인 item을 memb 개수만큼 stream에서 읽서 ptr에 저장
size_t fwrite(void *ptr, size_t size, size_t memb, FILE *stream);
// 크기가 size인 item을 memb 개수만큼 ptr에서 읽어 stream에 출력
- ptr: Pointer to buffer
- size: size of an item
- nmemb: number of items to read/write
- stream
Return: read/write한 item의 수 EOF: 파일 끝
[EX] Binary IO - fwrite()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
#include <stdlib.b>
int main(void){
char *fileName = "binary.bin";
int data[5] = {10, 20, 30, 40, 50};
FILE *fp = NULL;
if(!(fp=fopen(fileName, "wb"))){
fprintf(stderr, "Fail to open the file - %s\n", fileName);
exit(1);
}
size_t i = fwrite(data, sizeof(int), 5, fp);
printf("Success to write %zu object(s).\n", i);
fclose(fp);
return 0;
}
xxd
를 통해, 16진수로 바이너리 파일을 볼 수 있음
[EX] Binary IO - fread()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include <stdlib.h>
int main(void){
int buf[5] = {0};
FILE *fp = fopen("binary.bin", "rb");
if(!fp){
fprintf(stderr, "Fail to open the file - %s\n", "binary.bin");
exit(1);
}
size_t i = fread(buf, sizeof(int), 5, fp);
printf("Success to read %zu object(s).\n", i);
for (int i=0 ; i<5 ; i++){
printf("%d ", buf[i]);
}
fclose(fp);
return 0;
}
Ascii(Text) File vs Binary File
- Ascii(test) file
- 사람이 바로 읽을 수 있음
- 데이터 저장 및 사용시, 문자로 변환 과정이 필요함
- 많은 양의 데이터 처리에 비효율적
- 동일한 데이터를 저장하는 이진 파일 대비 많은 공간을 요구
- Binary file
- 컴퓨터가 바로 사용할 수 있는 형태(메모리에 저장된 형태 그래도 저장)
- 별도의 변화 과정 없이 읽기/쓰기 가능
- 데이터 처리에 효율적이며, 저장 공간을 효율적으로 사용할 수 있음
- 사람이 읽을 수 없는 형태 -> 데이터 교환시 Protocol이 필요
- 컴퓨터가 바로 사용할 수 있는 형태(메모리에 저장된 형태 그래도 저장)
[EX] 학생 정보 관리
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_NUM_STUDENT 255
enum action {ADD, FIND, EXIT};
typedef struct{
int ID;
char name[8];
float score;
}Student;
int fileOpen(FILE **_fp, char *_fileName, char *_mode);
int selectAction(void);
int printStudentInfo(Student *_info);
int addStudentInfo(FILE *_fp, Student *_info);
long findStudent(FILE *_fp, Student *_info);
int main(void){
FILE *fp = NULL;
Student data = {0};
fileOpen(&fp, "StudentDB", "ab+");
while(1){
switch(selectAction()){
case ADD:
addStudentInfo(fp, &data);
break;
case FIND:
if(findStudent(fp, &data)<0)
printf("Cannot find the student\n");
else
printStudentInfo(&data);
break;
case EXIT:
exit(0);
}
}
}
int selectAction(void){
int sel = 0;
printf("[%d]add [%d]find [%d]exit: ", ADD, FIND, EXIT);
scanf("%d", &sel);
return sel;
}
int printStudentInfo(Student *_info){
printf("%d %s %.2f\n", _info->ID, _info->name, _info->score);
}
int addStudentInfo(FILE *_fp, Student *_info){
printf("Enter ID Name Score : ");
scanf("%d %s %f", &_info->ID, (char*)&_info->name, &_info->score);
getchar();
fseek(_fp, 0, SEEK_END);
fwrite(_info, sizeof(Student), 1, _fp);
return 0;
}
long findStudent(FILE *_fp, Student *_info){
char name[255] = {0};
printf("Name: ");
scanf("%s", name);
getchar();
int elementSize = sizeof(Student);
fseek(_fp, 0, SEEK_SET);
while(!feof(_fp)){
fread(_info, elementSize, 1, _fp);
if(strcmp(_info->name, name)==0){
fseek(_fp, -elementSize, SEEK_CUR);
return ftell(_fp);
}
}
return -1;
}
int fileOpen(FILE **_fp, char *_fileName, char *_mode){
*_fp = fopen(_fileName, _mode);
if(!*_fp){
printf("Fail to open - %s\n", _fileName);
return -1;
}
return 0;
}
Formatted IO
1
2
3
4
5
6
#include <stdio.h>
int scanf(const char *format, ...);
int fscanf(FILE *stream, const char *format, ...);
int printf(const char *format, ...);
int fprintf(FILE *stream, const char *format, ...);
- format: 입출력 형식
- stream
Return: 입출력 한 문자 수 음수: error
[EX] Formatted I/O
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <stdio.h>
typedef struct{
int ID;
char name[8];
float score;
}Student;
int fileOpen(FILE **_fp, char *_fileName, char *_mode){
*_fp = fopen(_fileName, _mode);
if(!*_fp){
printf("Fail to open - %s\n", _fileName);
return -1;
}
return 0;
}
int main(void){
Student info = {0};
char *fileName = "StudentList.txt";
FILE *fp = NULL;
if(fileOpen(&fp, fileName, "a")<0) exit(1);
while(1){
// Get and store student info
printf("Enter ID Name Score (Exit: -1): ");
scanf("%d", &info.ID);
if(info.ID<0) break;
scanf("%s %f", &info.name, &info.score);
getchar();
fprintf(fp, "%d %s %.1f\n", info.ID, info.name, info.score);
}
fclose(fp);
if(fileOpen(&fp, fileName, "r")<0) exit(1);
int numStudent = 0;
float sum = 0;
// Read the Students' info and calculate the avg score
while(!feof(fp)){
fscanf(fp, "%d %s %f\n", &info.ID, &info.name, &info.score);
sum += info.score;
numStudent++;
}
printf("%d students, Avg = %.2f\n", numStudent, sum/numStudent);
fclose(fp);
return ;
}
Synchronizing with the disk
1
2
3
#include <stdio.h>
int fflush(FILE *stream);
Controlling buffering
1
2
3
#include <stdio.h>
int setvbuf(FILE *stream, char *buf, int mode, size_t size);
- Standard IO는 3가지 버퍼링 모드 지원
- Unbuffered(
_IONBF
)
- No user buffering performed
- Line-buffered(
_IOLBF
)
- Line-buffered(
- With new line character, the buffer is submitted to the kernel
- Default for streams connected to terminals (e.g, stdout, stdin)
- Block-buffered(
_IOFBF
)
- Block-buffered(
- Buffering is performed on a per-block basic
- Default for streams associated with files
- Unbuffered(
3강-5. File offset & File Pointer
Handling file offset
1
2
3
4
5
6
7
#include <stdio.h> // man -s 3 fseek
int fseek(FIlE *stream, long offset, int whence);
int ftell(FILE *stream);
void rewind(FILE *stream);
int fsetpos(FILE *stream, const fpos_t *pos); // 위치 지정
int fgetpos(FILE *stream, fpops_t *pos); // 지정된 위치 가져오기
- offset: 이동시킬 byte 수 (양수 or 음수)
- whence: 기준 위치 (
SEEK_SET, SEEK_CUR, SEEK_END
) - pos: offset을 저장 할(or 하고 있는)
fpos_t
주소
File Pointer <-> File Descripter
1
2
3
4
#include <stdio.h>
FILE* fdopen(int fd, const char *mode);
int fileno(FILE *stream);
- mode: 파일 열기 모드,
fd
를 열 때와 같은 종류여야 함
[EX] File pointer <- File Descriptor
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
int main(void){
FILE *fp;
int fd;
char str[BUFSIZ];
fd = open("hello.txt", O_RDONLY);
if (fd==-1){
perror("open");
exit(1);
}
fp = fdopen(fd, "r");
fgets(str, BUFSIZ, fp);
printf("Read : %s\n", str);
fclose(fp);
return 0;
}
[EX] File pointer -> File Descriptor
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
int main(void){
FILE *fp;
int fd, n;
char str[BUFSIZ];
fp = fopen("cookbook.txt", "r");
if(fp==NULL){
perror("fopen");
exit(1);
}
fd = fileno(fp);
printf("fd : %d\n", fd);
n = read(fd, str, BUFSIZ);
str[n] = '\0';
printf("Read: %s\n", str);
close(fd);
return 0;
}
Using a temporal file
1
2
3
4
5
6
#include <stdio.h>
char *tmpnam(char *s); // 중복되지 않는 임시 파일명 생성, 파일명만 생성하고 직접 열어야 함
char *tempnam(const char *dir, const char *pfx); // 디렉토리, 임시 파일 접수다
FILE *tmpfile(); // 임시 파일 포인터 생성, 파일명을 알 필요 없이, w+ 모드로 열린 파일 생성
Comments powered by Disqus.