시스템 프로그래밍 3주차는 2주차의 내용에 연장으로 표준 입출력에 대해 공부한다.
- Standard IO
Standard IO는 platform에 독립적인, user-buffering solution이다.
File pointer
File operation을 관리하는 구조체를 가리키는 포인터
내부적으로 file descriptor와 연동됨
Stream
프로그램과 file을 연결한 통로
파일 IO에 대한 일의 순서는
File open -> File access -> File close로
파일을 열고 사용을 한 후 파일을 닫는 순서이다.
- File open & close
Opening a file/stream
#include <stdio.h>
FILE *fopen(const char *path, const char *mode);
-path(file path)
열려는 파일의 경로
-mode(file open mode)
파일 열기 모드
return: file pointer (NULL: error)
파일 열기 모드(mode)
모드 | 파일이 존재하지 않을 때 | 파일이 존재 할 때 | |
r | 읽기 | 열기 실패 | 열기 성공 |
w | 쓰기 | 파일이 생성 | 덮어 씀(기존 내용은 지워 짐) |
a | 덧붙이기 | 파일이 생성 | 기존 파일 뒤에 기록 |
r+ | 읽기+ 모드 / 쓰기 모드로 전환 가능 | 읽기 <-> 쓰기 모드 전환 시, 반드시 fflush(), fseek(), fstepos(), rewind() 중 하나를 호출해야 함 |
w+ | 쓰기+ 모드/ 읽기 모드로 전환 가능 | |
a+ | 덧붙이기+ / 읽기 모드로 전환 가능 |
b | 이진 파일 모드 | 이진 파일 형식으로 파일을 연다 읽기/쓰기 모드는 위의 지정자들로 지정 |
이진 파일은 사람이 읽을 수 있는 문자로 채워진 Ascii 파일과 다르게 이진 데이터가 직접 저장된 파일을 말한다.
Closing a files/streams
#include <stdio.h>
int fclose(FILE *stream);
-stream
닫으려는 stream
return: 0(-1: error)
열고 닫는 방법을 배웠으니 바로 한 번 해보도록 하자.
#include <stdio.h>
#include <stdlib.h>
int main(void){
FILE *fp;
if ((fp = fopen("hello.txt", "w")) == NULL){
perror("fopen: hello.txt");
exit(1);
}
fclose(fp);
return 0;
}
딱 파일만 열고 바로 닫는 예제이다.
실행을 해보면 w 모드이기 때문에
없던 hello.txt 파일이 생성되는 것을 볼 수 있다.
- File read & write
파일을 열고 닫는 방법을 공부했으니 이제 순서의 중간인 파일의 사용에 대해 공부해보자
Character-based
Chararcter-based reading
#include <stdio.h>
int fgetc(FILE *stream);
int getc(FILE *stream); //macro 형태로 구현되어 있어, 인자에서 계산은 X
int getchar(void); //getc(stdin), 표준입력에서 입력을 받음
-stream
File operation을 수행할 stream
return : 읽은 문자 (-1: error)
파일로부터 문자 하나씩 읽는 방법이다.
Character-based writing
#include <stdio.h>
int fputc(int c, FILE *stream);
int putc(int c, FILE *stream);
int putchar(int c); //putc(c, stdout)
-stream
File operation을 수행할 stream
-c
쓰려는 문자
return: 기록한 문자(-1:error)
Characted-based로 실습을 해보자
#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){ //하나씩 계속 읽어서 hello.out에 작성
fputc(c, wfp);
}
fclose(rfp);
fclose(wfp);
return 0;
}
이렇게 파일을 작성하면 hello.txt에서 문자를 하나씩 읽어서 hello.out에 저장하게 된다.
String-based
String-based reading
#include <stdio.h>
char *gets(char *s); //get from stdin
char fgets(char *s, int n, FILE *stream);
-s
읽은 문자열을 저장할 buffer
-n
buffer의 크기
-stream
File operation을 수행할 stream
return: Buffer의 시작 주소(NULL: error)
String-based writing
#include <stdio.h>
int puts(char *s); //put to stdout
int fputs(char *s, FILE *stream);
-s
기록할 문자열을 저장한 buffer
-stream
File operation을 수행할 stream
return: 양수(음수: error)
문자열 단위로 실습을 해보자
#include <stdio.h>
#include <stdlib.h>
int main(void){
FILE *rfp, *wfp;
char buf[BUFSIZ];
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.txt");
exit(1);
}
while(fgets(buf, BUFSIZ, rfp) != NULL) fputs(buf, wfp);
fclose(rfp);
fclose(wfp);
return 0;
}
문자열 단위로 읽어와서 hello.out에 저장한다.
이렇게 문자열 단위로 잘 가져오는 것을 볼 수 있다.
Binary IO
#include <stdio.h>
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
-ptr
Pointer to buffer
-size
size of an item
-nmemb
number of items to read/write
-stream
File operation을 수행 할 stream
return: read/write 한 item의 수(EOF: 파일의 끝)
이진 파일을 작성하는 예제이다.
#include <stdio.h>
#include <stdlib.h>
int main(void){
char *fileName = "binary.bin";
int data[5] = {10, 20, 30, 40, 50};
FILE *fp;
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 %d objects.\n", i);
fclose(fp);
return 0;
}
그러고 cat으로 파일을 확인해보면 이진파일이기 때문에 알 수 없는 내용이 출력된다.
해당 이진파일의 내용을 그대로 보고 싶다면
xxd binary.bin을 사용하면 된다.
이제 이진파일을 읽어보자
#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 %d object\n", i);
for(int i = 0; i < 5; i++) printf("%d ", buf[i]);
fclose(fp);
return 0;
}
이렇게 이진파일을 정수로 읽어오는 것을 볼 수 있다.
이진 파일은 사람이 바로 읽을 수 없지만 동일한 데이터를 저장하는 데 Ascii 파일보다 적은 공간을 요구한다는 장점이 있다.
Formatted IO
#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
File operation을 수행 할 stream
return: 입출력 한 문자의 수(음수: error)
자세히 볼 필요도 없을만큼 많이 사용한 함수들이다.
바로 작성을 하는 예제이다.
#include <stdio.h>
#include <stdlib.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){
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);
return 0;
}
-1을 입력할 때까지 입력을 받고 StudentList.txt에 작성을 하는 예제이다.
이렇게 StudentList.txt를 만들어 그곳에 작성을 하는 것을 볼 수 있다.
이제 이 파일을 읽어보자
#include <stdio.h>
#include <stdlib.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, "r") < 0) exit(1);
int numStudent = 0;
float sum = 0;
while(!feof(fp)){
fscanf(fp, "%d %s %f\n", &info.ID, &info.name, &info.score);
sum += info.score;
numStudent++;
}
printf("%d students, Average = %2.f\n", numStudent, sum / numStudent);
fclose(fp);
}
해당 파일을 잘 읽는 것을 볼 수 있다.
Synchronizing with the disk
#include <stdio.h>
int fflush(FILE *stream);
-stream
File operation을 수행 할 stream
return: 0(-1:error)
stream에 있는 내용들을 바로 디스크에 작성하는 방법
- File offset & File pointer
Handling file offset
#include <stdio.h>
int fseek(FILE *stream, long offset, int whence);
int ftell(FILE *stream);
void rewind(FILE *stream);
int fsetpos(FILE *stream, const fpost_t *pos);
int fgetpos(FILE *stream, fpost_t *pos);
-stream
File operation을 수행 할 stream
-offset
이동시킬 byte 수(양수, 음수 모두 가능)
-whence
기준 위치(SEEK_SET, SEEK_CUR, SEEK_END)
-pos
offset을 저장 할 fpost_t 주소
File Pointer <-> File Descriptor
#include <stdio.h>
FILE *fdopen(int fd, const char *mode);
-fd
file descriptor
-mode
파일 열기 모드
return: File pointer(NULL: error)
#include <stdio.h>
int fileno(FILE *stream);
-stream
변환 할 stream
return: File descriptor(-1: error)
서로 pointer와 descriptor간의 변환하는 함수이다.
'학교 생활 > 시스템 프로그래밍' 카테고리의 다른 글
시스템 프로그래밍 6주차 (0) | 2023.03.07 |
---|---|
시스템 프로그래밍 5주차 (0) | 2023.03.05 |
시스템 프로그래밍 4주차 (0) | 2023.03.02 |
시스템 프로그래밍 2주차 (2) | 2023.01.15 |
시스템 프로그래밍 1주차 (0) | 2023.01.14 |