19.파일 입출력
표준 입출력 라이브러리(깊게 x 간단하게)
표준 입출력 라이브러리란?
표준 입출력 라이브러리는 데이터의 형식에 따라 다른 함수를 제공합니다. 프로그램이 사용하는 데이터 형식은 ‘텍스트(문자열)’와 ‘바이너리(이진)’로 나누어지는데, 자신이 다루는 데이터가 텍스트 형식이면 텍스트 관련 함수를 사용해야 하고 바이너리 형식이면 바이너리 관련 함수를 사용해야 합니다.
텍스트 기반 파일 : html 등 바이너리 : 한글파일, 이미지 파일 등
변수에 저장된 데이터의 크기를 구할 때의 차이점
예를들어 temp 변수에 저장된 데이터의 크기를 구하는 경우에 바이너리 속성은 메모리의 크기를 구해야 변수의 크기를 구할 수 있기 때문에 sizeof 연산자 사용
int data_size = sizeof(temp); //변수 크기를 구함. data_size에는 값 8이 저장됨
반면에 문자열 속성은 temp 변수에 저장된 문자열의 길이를 구하면 변수의 크기를 구할 수 있기 때문에 strlen 함수를 사용하여 문자열 길이를 구한다.
int data_size = strlen(temp); //문자열 길이를 구함. data_size에는 값 3이 저장
변수에 저장된 값을 다른 변수에 복사할 때 차이점
바이너리 모드는 변수에 들어있는 값을 그대로 복사하는 개념을 사용하기 때문에 temp 변수의 값을 dest 변수로 복사하려면 memcpy 함수를 사용. (통째로 복사)
char temp[8] = {'a','b','c',0.};
char dest[8];
memcpy(dest, temp, sizeof(temp)); //temp에서 dest로 8바이트 크기만큼 메모리 복사
반면에 문자열 속성은 temp 변수에 저장되어 있는 문자열만 복사하면 되기 때문에 strcpy 함수 사용(쓸 만큼만)
char temp[8] = {'a','b','c',0.};
char dest[8];
strcpy(dest, temp); //temp에서 dest로 4바이트 크기(NULL 문자 0까지 포함)만큼 복사
파일 열기와 닫기
파일 입출력 함수의 도우미! FILE 구조체
표준 입출력 라이브러리는 ‘FILE 구조체’ 로 포인터 변수를 선언하고 파일 입출력 함수를 호출할 때마다 이 변수를 넘겨주도록 만들어져 있습니다. FILE 구조체는 사용하려는 디스크 상의 파일이 어떤 상태로 사용 중인지에 대한 정보를 담고 있으며, 파일을 좀 더 편하게 사용할 수 있도록 도와준다.(읽고 쓸 때 반드시 필요함)
FILE *p_file;
//파일열기 생략함
fseek(p_file, 0, SEEK_SET);
파일열기 : fopen 함수 (기존에 만들어져있는 함수를 읽어올 때)
파일을 성공적으로 열면 FILE * 형식의 메모리 주소 값을 반환한다. 만약 파일이 존재하지 않거나 파일 형식을 잘못 사용해서 파일 열기에 실패한다면 NULL 값을 반환한다.
FILE *p_file = fopen("tipssoft.dat", "r"); //주소값은 #define or const or 상수로 몰아넣어줘야 한다!!! 무조건!!!
if(p_file != NULL){
//파일 열기에 성공한 경우
}else{
//파일 열기에 실패한 경우
}
주소값은 #define or const or 상수로 몰아넣어줘야 한다!!! 무조건!!! 더 좋은건 파일 이름과 경로를 분리해서 정의하는게 좋다
파일이 다루는 속성에 따라 기본적으로 나누어지는 형식이다. 아래의 두가지 형식은 단독으로 쓸 수는 없고 앞으로 배우게 될 다른 형식들과 함께 써야한다.
| 형식 | 설명 |
|---|---|
| t | 텍스트 속성으로 파일을 사용하겠다는 뜻. 만약 이 형식으로 바이너리 파일을 열면 파일 열기는 성공하겠지만 파일 입출력 함수를 사용하면 오류 발생. |
| b | 바이너리 속성의 파일을 사용한다는 뜻. 이 형식이 기본값. |
파일 내용 읽기 모드 “r”
파일의 내용을 읽기 위한 목적으로 파일을 연다. 만약 파일이 없으면 파일 열기에 실패하고 NULL값을 반환.
FILE *p_file = fopen("tips.dat", "rb"); //rb대신 r만 사용해도 됨. 바이너리 파일을 여는 경우
FILE *p_file = fopen("tips.dat", "rt"); //텍스트 파일 열 때.
파일 데이터 쓰기 모드 “w” (파일을 저장. 파일을 생성해준다고 생각. 덮어쓰기라고 생각)
데이터를 쓰기 위한 목적. 만약 명시한 파일이 작업 경로에 없다면 그 이름으로 파일을 만든 후에 파일을 열기대문에 파일열기에 실패하지 않는다. 하지만 같은 이름을 가진 파일이 이미 존재하는 경우에는 파일을 열면서 그 파일이 가지고 있던 내용을 모두 지우고 시작하기 때문에 주의해야한다.
FILE *p_file = fopen("tips.dat", "wb"); //wb대신 w만 사용해도 됨. 바이너리 파일을 여는 경우
FILE *p_file = fopen("tips.dat", "wt"); //텍스트 파일 열 때.
그러나 디스크에 용량이 부족해서 파일을 만들 수 없거나, CD와 같이 읽기 전용 디스크에 쓰기 모드로 사용하면 파일열기에 실패한다.
파일에 데이터 이어쓰기 모드 “a” (이어쓰기. 추가.)
만약 파일이 작업 경로에 없다면 그 이름으로 파일을 만든 후에(w와 같음) 파일을 연다. 하지만 w와 달리 기존에 파일이 존재하더라도 파일 내용을 지우지 않고 기존 파일 내용에 이어쓰기한다.
FILE *p_file = fopen("tips.dat", "ab"); //ab대신 a만 사용해도 됨. 바이너리 파일을 여는 경우
FILE *p_file = fopen("tips.dat", "at"); //텍스트 파일 열 때.
이 형식도 디스크에 용량이 부족하거나 읽기 전용 디스크에 사용하면 파일 읽기에 실패한다.
파일 사용 형식에서 읽기와 쓰기 같이 사용하기
읽기 강조 “r+”
이 형식으로 파일을 여는 경우 기존 파일이 없으면 파일을 새로 만들지 않고 파일 읽기에 실패. 기존 파일이 있는 경우 해당 파일의 내용을 지우지는 않지만 기존 데이터의 위치로 이동해서 해당 위치의 내용을 덮어 쓸 수 있다. 바이너리 파일에 사용하는 경우 “r+, rb+, r+b” 텍스트 파일에 사용하는 경우 “rt+, r+t”
쓰기 강조 “w+”
파일 여는 경우 기존 파일이 없으면 파일을 열 때 새로만들고, 파일이 이미 존재하면 기존파일의 내용을 모두 지우고 시작. 바이너리 “w+, wb+ w+b” 텍스트 “wt+, w+t”
읽기와 이어쓰기를 같이 사용하기 “a+”
읽기와 이어쓰기 모드 같이 사용하는 경우에 사용하며 확장을 더 강조한다.확장을 더 강조한다는 뜻은 이 형식으로 파일을 여는 경우에 기존 파일이 없으면 파일을 새로 만들고 파일이 존재하면 파일의 내용을 지우지 않고 기존 내용에 이어서 시작한다. 쓰기를 사용하면 현재 위치와 상관 없이 파일의 끝에 내용이 추가된다. r+, w+
읽기+쓰기 -> 메모장에서 내용 확인 후 내용 변경(찾아 바꾸기)
파일 닫기 : fclose 함수
fopen 사용이 끝나면 fclose 함수를 사용하여 파일을 닫아야 한다. (fopen 했으면 fclose 무조건!)
텍스트 파일에 데이터 읽고 쓰기
void main(){
FILE *p_file = fopen("abc.txt", "wt") //쓰기 모드로 텍스트 파일 오픈
if(NULL != p_file){ //파일 열기에 성공한 경우
fprintf(p_file, "HELLO");
fclose(p_file); //파일 닫기
}
}
//해당 파일이 없으면 텍스트파일 자동으로 생성 후 문자열 저장한다.
텍스트 파일에 문자열 저장하기 : fprintf
텍스트 파일에 문자열 읽기 : fscanf
fscanf 함수는 기본적으로 공백문자를 만나면 다음 입력이 시작된 것으로 처리.
예제) fscanf 함수로 문자열 형식의 정수 값 모두 읽어오기
void main(){
int num;
FILE *p_file = fopen("abc.txt", "rt");
if(NULL != p_file){
while(EOF != fscanf(p_file, "%d", &num)){ //텍스트 파일의 끝(End Of File). 따라서 EOF를 반환할 때까지 반복하면서 숫자값을 읽어온다.
printf("%d", num);
}
fclose(p_file);
}
}
텍스트 파일에서 한줄 단위로 문자열 읽기 : fgets
한 줄 단위로 문자열을 처리하고 싶은 경우 사용.
void main(){
char temp[64];
FILE *p_file = fopen("abc.txt", "rt");
if(NULL != p_file){
while(NULL != fgets(temp, sizeof(temp), p_file)){
printf("%s", temp);
}
fclose(p_file);
}
}
바이너리 파일에 데이터 읽고 쓰기
바이너리 파일에 데이터 저장하기 : fwrite
함수 원형 : size_t fwrite(const void *buffer, size_t size, size_t count, FILE *stream); 사용 형식 : fwrite(저장할 데이터의 시작주소, 저장할 데이터의 기준 단위 크기, 반복 횟수, 파일 포인터);
void main(){
int data = 0x0000412;
FILE *p_file = fopen("abc.txt", "wb");
if(NULL != p_file){
fwrite(&data, sizeof(int), 1, p_file);
fclose(p_file);
}
}
반복횟수의 용도
int data[5] = {0,1,2,3,4};
fwrite(data, sizeof(int), 5, p_file); //위아래 같음
fwrite(data, sizeof(int) * 5, 1, p_file); //위아래 같음. 이렇게 사용하는 경우가 더 많음
바이너리 파일에서 데이터 읽기 : fread
p520. 필기 별로 없음 안중요한듯.. 손목아픔
파일 내부의 작업 위치 탐색하고 확인하기 : fseek(중요!), ftell
해당 하무를 사용하여 원하는 위치로 건너 뛸 수 있다.
함수 원형 : int fseek(FILE *stram, long offset, int origin); 사용 형식 : fseek(파일 포인터, 이동거리, 기준 위치);
이 함수는 파일의 데이터를 읽을 기준 위치로 SEEK_SET(파일의 시작), SEEK_END(파일의 끝), SEEK_CUR(현재 위치)를 사용할 수 있고 지정한 기준 위치로부터 사용자가 지정한 이동거리만큼 이동한다. 이동 거리는 양수를 명시하면 지정한 기준 위치에서 뒤로 이동하며 음수를 명시하면 앞으로 이동.
이렇게 이동한 위치를 값으로 확인하고 싶으면 ftell 사용.
예제) fseek 함수와 ftell 함수로 바이너리 파일 크기 알아내기 ->이거는 복잡한 방식이니 이런게 있구나하고 넘겨라.
20. 함수 포인터
int (*p)(int, int); //sum 함수를 가리킬 수 있는 함수 포인터를 선언.(함수 원형을 담은 포인터 선언)
p = &Sum //sum 함수의 주소를 p에 저장
이렇게 함수 포인터를 선언하여 저장했다면 다음과 같이 호출
int result = (*p)(2,3); //int result = Sum(2,3)l 과 같음. 5 저장
콜백 함수 (콜백하려면 함수 포인터 사용해야 함. 개념을 이해!)
타이밍을 알 수 없을 때 미리 요청해두고, 해당 시기가 되면 자동으로 처리 해서 값을 나에게 다시 던져준다?
Comments