Home [SP] 표준 입출력
Post
Cancel

[SP] 표준 입출력

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 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가지 버퍼링 모드 지원
    1. Unbuffered(_IONBF)
    • No user buffering performed
      1. Line-buffered(_IOLBF)
    • With new line character, the buffer is submitted to the kernel
    • Default for streams connected to terminals (e.g, stdout, stdin)
      1. Block-buffered(_IOFBF)
    • Buffering is performed on a per-block basic
    • Default for streams associated with files

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+ 모드로 열린 파일 생성
This post is licensed under CC BY 4.0 by the author.

[DSP] Section 7: Audio Features

23년 ~4월 4주차 주간 회고

Comments powered by Disqus.