제 15장 파일 처리
01. 파일 기초
02. 텍스트 파일 입출력
03. 이진 파일 입출력
04. 파일 접근 처리
파일의 유형: 텍스트 파일 or 이진 파일
함수 fopen(): 파일 스트림 열기
함수 fopen()과 fopen_s() 함수원형
FILE * fopen(const char * _Filename, const char * _Mode);
errno_t fopen_s(FILE ** _File, const char * _Filename, const char * _Mode);
함수 fopen()은 파일명 _Filename의 파일 스트림을 모드 _Mode로 연결하는 함수이며, 스트림 연결에 성공하면 파일포인터를 반환하며, 실패하면 NULL을 반환한다.
함수 fopen_s()는 스트림 연결에 성공하면 첫 번째 인자인 _File에 파일 포인터가 저장되고 정수 0를 반환하며, 실패하면 양수를 반환한다. 현재 Visual C++에서는 함수 fopen_s()의 사용을 권장하고 있다.
함수 fclose(): 파일 스트림 닫기
함수 fclose()
int fclose(FILE * _File);
fclose(f);
함수 fclose()는 파일 스트림 f를 닫는 함수로서, 성공하면 0을 실패하면 EOF를 반환한다.
ex1)이름과 성적 정보 내용으로 간단한 파일을 생성
#include <stdio.h>
#include <stdlib.h>
int main(){
char* fname = "basic.txt"; //파일명
FILE* f; //파일 포인터
char name[30] = "0202"; //파일에 쓰려는 자료
int point = 99;
/*함수 fopen()의 호출 시, 첫 인자는 파일이름, 두 번째 인자는 모드로 "w"는 쓰기 모드이며, 반환 값을 파인 포인터로 선언한 f에 대입, 만일 파일 열기에 실패하면 f에 NULL이 저장됨*/
if((f=fopen(fname,"w"))==NULL){
printf("파일이 열리지 않아 종료합니다.\n");
exit(1); //파일 열기에 실패하면 메시지 출력하고 exit(1)으로 종료
};
//파일 "basic.txt"에 쓰기
fprintf(f, "이름 %s 학생의 성적은 %d 입니다. \n", name, point); //지정한 파일 f에 출력 printf()를 하는 기능을 수행
fclose(f); //파일 처리가 종료되었으면 fclose()로 스트림을 닫음
//표준출력 콘솔에 쓰기
printf("이름 %s 학생의 성적은 %d 입니다. \n", name, point);
puts("프로젝트 폴더에서 파일 basic.txt를 메모장으로 열어 보세요");
return 0;
}
ex2) "myinfo.txt"에 데이터 출력
#include <stdio.h>
#include <stdlib.h>
int main(){
FILE * f; //파일 포인터
//if (fopen_s(&f, "myinfo.txt", "w") != 0) 이런 식으로 써도 됨
if((f=fopen("myinfo.txt","w"))==NULL){
printf("파일이 열리지 않습니다\n");
exit(1);
};
//파일에 쓰려는 자료
char tel[15] = "010-1111-1111";
char add[20] = "대구광역시"
int age = 24;
fprintf(f, "전화번호: %s, 주소: %s, 나이: %d \n", tel, add, age);
fclose(f); //파일 닫기
//표준 출력 콘솔에 쓰기
printf("전화번호: %s, 주소:%s, 나이: %d\n", tel, add, age);
puts("프로젝트 폴더에서 파일 myinfo.txt를 메모장으로 열어보세요.");
return 0;
}
텍스트 파일에 자료를 쓰거나 읽기 위한 함수
함수 fprintf()와 fscanf() 함수 원형
int fprintf(FILE * _File, const char * _Format, ...);
int fscanf(FILE * _File, const char * _Format, ...);
int fscanf_s(FILE * _File, const char * _Format, ...);
이 함수에서 _File은 서식화된 입출력 스트림의 목적지인 파일이며, _Format은 입출력 제어 문자열이며, 이후 기술되는 인자는 여러 개의 출력될 변수 또는 상수이다.
scanf_s("%s%d%d", name. 30, &point1, &point2);
fprintf(f, "%d %s %d %d \n", ++cnt, name, point1,point2);
fscanf_s(f, "%d %s %d %d\n", &cnt, name, 30, &point1, &point2);
//문자열이 저장되는 name과 그 크기(30)를 지정해야 한다.
기호 상수 stdin, stdout, stderr
ex1) 이름과 성적 2개를 입력해 그 내용을 파일에 쓰고 다시 읽어 표준 출력에 보이는 프로그램
#include <stdio.h>
#include <stdlib.h>
int main(){
char fname[] = "grade.txt";
FILE* f;
char name[30];
int point1, point2, cnt = 0;
/* 첫 번째 인자는 파일 포인터의 주소, 두 번째 인자는 파일이름, 세 번째 인자는 모드로 "w"는 쓰기 모드이며, 반환값은 오류 수로 0이 아니면 파일 열기에 문제가 발생한다 */
if(fopen_s(&f,fname,"w") != 0){
printf("파일이 열리지 않습니다. \n");
exit(1); //파일 열기에 실패하면 메시지 출력하고 exit(1)으로 종료
};
printf("이름과 성적(중간, 기말)을 입력하세요. \n");
scanf("%s %d %d", name, &point1, &point2);
//scanf_s("%s%d%d, name, 30, &point1, &point2);
fprintf(f, "%d %s %d %d \n", ++cnt, name, point1, point2);
//파일 "grade.txt"에 쓰기
fclose(f);
//if((f=fopen(fname, "r")) == NULL)
if(fopen_s(&f, fname, "r") != 0){
printf("파일이 열리지 않습니다. \n");
exit(1);
};
//파일 "grade.txt"에서 읽기
fscanf(f, "%d %s %d %d\n", &cnt, name, &point1, &point2);
//fscanf_s(f, "%d %s %d %d\n", &cnt, name, 30, &point1, &point2);
//표준출력에 쓰기
fprintf(stdout, "\n%6s%16s%10s%8s\n", "번호", "이름", "중간", "기말");
fprintf(stdout, "%5d%18s%8d%8d\n", cnt, name, point1, point2);
//함수 fprintf(stdout, ...)은 함수 printf(...)로도 이용 가능
fclose(f);
//파일 읽기가 종료되었으면 fclose()로 스트림 닫기
return 0;
}
ex1)
코드 작성: 파일 출력
정수 난수(0~99) 10개를 numbers.txt에 출력
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(){
FILE* fp;
int num;
int i;
srand((unsigned)time(NULL));
fp = fopen("numbers.txt", "w");
if(fp == NULL){
printf("파일을 열 수 없습니다.\n");
return 1;
}
for( i=0; i<10; i++){
num=rand() % 100;
fprintf(fp, "%d\n", num);
}
fclose(fp);
printf("파일 쓰기 완료\n");
return 0;
}
ex2)
코드 작성: 파일 입력
numbers.txt에서 정수 데이터 읽어 누적합 계산
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(){
FILE* fp;
int sum = 0;
int num;
fp = fopen("numbers.txt", "r");
if(fp == NULL){
printf("파일을 열 수 없습니다.\n");
return 1;
}
while(fscanf(fp, "%d", &num)==1){
sum += num;
}
printf("%d\n", sum);
fclose(fp);
return 0;
}
함수 fgets()와 fputs()
함수 fgets()와 fputs() 함수 원형
char * fgets(char * _Buf, int _MaxCount, FILE * _File);
int fputs(char * _Buf, FILE * _File);
함수 fgets()는 _File로부터 한 행의 문자열을 _MaxCount 수의 _Buf 문자열에 입력 수행
함수 fputs()는 _Buf 문자열을 _File에 출력 수행
char names[80];
FILE *f;
fgets(names, 80, f);
fputs(names, f);
//fscanf는 워드 단위로 입력을 받지만 fgets은 엔터 키 들어올 때 까지 라인 단위로 입력을 받음
함수 feof()와 ferror()
함수 feof()와 ferror() 함수원형
int feof(FILE * _File);
int ferror(FILE * _File);
함수 feof()은 _File의 EOF를 검사
함수 ferror()는 _File에서 오류발생 유무를 검사
while(!feof(stdin)){
...
fgets(names, 80, stdin); //표준입력
}
ex1)
여러 줄에 걸쳐 이름, 성적을 입력하여 지정된 이름의 파일에 그 내용을 모두 저장
#include <stdio.h>
#include <stdlib.h>
int main(){
char fname[] = "grade.txt";
char names[80];
int cnt = 0;
FILE* f;
// if((f=fopen(fname, "w"))==NULL)
if(fopen_s(&f, fname, "w") != 0){
printf("파일이 열리지 않습니다. \n");
exit(1);
};
printf("이름과 성적(중간, 기말)을 입력하세요. \n");
fgets(names, 80, sdin); //표준입력으로 받은 한 행을 변수 names에 저장, 한 행이 79열보다 크면 배열 names[]의 크기를 더 크게 조정
/*표준입력으로 여러 줄에 걸쳐 적당한 형태로 입력하고 마지막 행에는 반드시 키 ctrl+Z를 입력한 후 Enter을 친다!
ctrl+Z를 입력하기 전 Enter을 치면 Enter값 역시 입력이 됨*/
//콘솔에 이름 중간 기말 입력하고 Enter치고
//계속 여러 줄에 여러 학생의 성적을 입력하다가
//종료하고 싶을 때 새로운 줄 처음에 ctrl + Z 누르고 Enter 치면 종료
//표준입력이 있으면 계속, ctrl+Z로 feof()이면 종료 -- while()문 반복 조건 설명
while(!feof(stdin)){ //ctrl+Z 표준입력의 종료 확인
//파일 "grade.txt에 쓰기
fprintf(f, "%d ", ++cnt); //맨 앞에 번호를 삽입
fputs(names, f); //이후에 입력 받은 이름과 성적 2개 저장
fgets(names, 80, stdin); //다시 표준입력
}
fclose(f);
return 0;
}
ex2)
#include <stdio.h>
#include <stdlib.h>
int main(){
char fname[] = "grade.txt";
char names[80];
int cnt = 0;
FILE* f;
f = fopen(fname,"w");
if(f==NULL){
printf("파일이 열리지 않습니다.\n");
exit(1);
}
printf("이름과 성적(중간, 기말)을 입력하세요.\n");
/*
while(fgets(names, sizeof(names), stdin) != NULL && names[0] != '\n'):
- fgets 함수는 stdin으로부터 입력을 받아서 names 배열에 저장해요.
- sizeof(names)는 names 배열의 크기를 의미해요. 즉, 한 번에 names 배열 크기만큼의 문자열을 입력받아요.
- != NULL은 입력이 성공했는지 확인하는 부분이에요. fgets는 입력이 성공하면 names 포인터를 반환하고, 실패하면 NULL을 반환해요.
- names[0] != '\n'는 빈 줄(Enter만 입력된 줄)이 입력되었는지 확인해요. 빈 줄이 입력되면 반복문을 종료해요.
*/
while(fgets(names, sizeof(names), stdin) != NULL && names[0] != '\n'){
fprintf(f, "%d ", ++cnt);
fputs(names, f);
}
fclose(f);
return 0;
}
함수 fgetc()와 fputc()
함수 fgetc()와 fputc() 함수원형
int fgetc(FILE * _File);
int fputc(int _Ch, FILE * _File);
int getc(FILE * _File);
int putc(int _Ch, FILE * _File);
함수 fgetc()와 getc()는 _File에서 문자 하나를 입력받는 함수
함수 fputc()와 putc()문자_Ch를 파일 _File에 출력하는 함수
//fgetc는 파일에서 문자 단위로!!
예제: 문자 단위 출력
ex1) 지정한 파일"05flist.c"의 내용을 행 번호를 붙여 표준 출력으로
#include <stdio.h>
#include <stdlib.h>
int main(void){
FILE* f;
//if((f=fopen("05flist.c","r')) == NULL)
if(fopen_s(&f, "05flist.c","r")!=0){ //읽기 모드로 파일 열기
printf("파일이 열리지 않습니다. \n");
exit(1);
}
int ch, cnt = 0; //문자를 저장할 ch, 행 번호를 저장할 cnt
printf("%4d ", ++cnt); //1행 처음에 번호 1 출력
while((ch=fgetc(f))!=EOF){
putchar(ch); //putc(ch, stdout);
if(ch=='\n'){ //2행부터 행 처음에 행 번호 출력
printf("%4d: ", ++cnt); //새로운 줄에 이동했으면 다시 처음에 행 번호 ++cnt 출력
}
printf("\n");
fclose(f);
return 0;
}
ex2) 입력 파일 lab2uplowerchar.c의 내용을 읽어서 문자들의 대소문자를 반대로 바꾸고, 결과를 convertchar.c 파일에 저장하는 프로그램이에요.
#include <stdio.h>
#include <stdlib.h>
#include <stdlib.h>
#include <ctype.h>
int main(void){
FILE* f1, *f2;
if((f1=fopen("lab2uplowerchar.c", "r"))==NULL){
printf("cannot open this file\n");
exit(1);
}
if((f2=fopen("convertchar.c","w"))==NULL){
printf("cannot open this file\n");
fclose(f1);
exit(1);
}
char a;
// 파일 내용을 한 문자씩 읽고 처리
while((a=getc(f1))!=EOF){
if(isalpha(a)){
// 소문자이면 대문자로 변환
if(islower(a)){
a=toupper(a);
}
// 대문자이면 소문자로 변환
else if(isupper(a)){
a = tolower(a);
}
// 변환된 문자를 출력 파일에 쓰기
putc(a,f2); //fputc(a,f2);
}
}
fclose(f1);
fclose(f2);
printf("File convertchar.c is created !!!\n");
return 0;
}
제 11장 포인터 기초
01. 포인터 변수와 선언
02. 간접연산자 *와 포인터 연산
03. 포인터 형변환과 다중포인터
04. 포인터를 사용한 배열 활용
주소 개념 고유한 주소(address)
-메모리 주소는 저장 장소인 변수이름과 함께 기억장소를 참조하는 또 다른 방법
포인터 변수? 메모리의 주소를 저장할 수 있는 변수
포인터 변수는 그 메모리에 해당되는 데이터 타입에 따라 자료형이 결정됨
여러 포인터 변수 선언과 NULL 주소값 대입
int *ptr1, *ptr2, *ptr3; //ptr1, ptr2, ptr3모두 int형 포인터다!!(포인터는 *을 써야함)
int *ptr1, ptr2, ptr3; //ptr1은 int형 포인터지만 ptr2와 ptr3는 int형 변수임
int *ptr = NULL;
#define NULL ((void*)0)
간접연산자(indirection operator) *을 사용한 역참조
p는 그 메모리 주소를, *p는 그 메모리 주소에 있는 값(==즉, 데이터 변수 그 자체)을 쓸 수 있다
포인터 p가 가리키는 변수가 data라면 *p는 변수 data를 의미
*p로 data 변수 저장 장소인 l-value 와 참조 값인 r-value로 참조 가능
변수 data로 가능한 작업은 *p로도 가능
ex) *p = 200;
tip) 주소연산자 &와 간접연산자 *
주소 연산 '&변수'는 변수의 주소값이 결과값
간접 연산 '*포인터변수'는 포인터 변수가 가리키는 변수 자체가 결과값
공통점
모두 전위 연산자.
차이점
'*포인터 변수'는 l-value와 r-value로 모두 사용이 가능하나, 주소값인 '&변수'는 r-value로만 사용이 가능하다.
'*포인터 변수'와 같이 간접연산자는 포인터 변수에만 사용이 가능하나, 주소연산자는 '&변수'와 같이 모든 변수에 사용이 가능하다.
포인터 변수의 연산
주소 연산 : 간단한 더하기와 뺄셈 연산으로 이웃한 변수의 주소 연산을 수행
절대적인 주소의 계산이 아니며, 변수 자료형의 상대적인 위치에 대한 연산
ex)
int main(){
int arr[] = (1,2,3,4,5};
int length = sizeof(arr) / sizeof(arr[0]);
int* p = arr;
for (int i = 0; i< length; i++){
printf("%p : %d \n", (p+i), *(p+i));
}
return 0;
}
ex2) 포인터를 이용하여 두 수의 값을 교환하는 프로그램
void swap(int* x, int* y){
int t;
t=*x;
*x=*y;
*y=t;
}
int main(){
int num1, num2;
scanf_s("%d%d", &num1, &num2);
swap(&num1, &num2);
printf("%d, %d\n", num1, num2);
return 0;
}
변수의 내부 저장 표현
명시적 형변환
포인터 자료형의 변환
'공부 > C' 카테고리의 다른 글
C언어로 배우는 프로그래밍 기초 10주차 (0) | 2024.07.11 |
---|---|
C언어로 배우는 프로그래밍 기초 9주차 (2) | 2024.07.10 |
C언어로 배우는 프로그래밍 기초 6주차 (1) | 2024.07.05 |
C언어로 배우는 프로그래밍 기초 5주차 (1) | 2024.07.04 |
C언어로 배우는 프로그래밍 기초 4주차 (0) | 2024.07.04 |