제 7 장 C 표준파일입출력 리눅스시스템프로그래밍 청주대학교전자공학과 한철수 1
목차 파일및파일포인터 텍스트파일 이진파일 임의접근 버퍼입출력 기타함수 2
7.1 절 시스템호출과 C 라이브러리함수 응용프로그램이커널에서비스를요청하는방법 방법 1: 시스템호출 (system call) 을이용하여요청함. 방법 2: C 언어가제공하는라이브러리함수를이용하여요청함. 라이브러리함수내에서시스템호출을이용하고있음. 시스템호출을보다쉽게사용할수있도록포장하였음. 3
7.1 절 C 의파일 C 언어가제공하는파일입출력을이해하기위해서는 C 파일에대한이해가필요함. C 파일은모든데이터를연속된바이트형태로저장함 저장된데이터의종류에따라텍스트파일과이진파일로구분함. 4
7.1 절 텍스트파일과이진파일 텍스트파일 (text file) 문자들만으로이루어진파일 한글, 영문, 숫자등의문자들을포함하고있음. 여러개의줄로이루어지며매줄마다줄의끝을나타내는개행문자 ( \n ) 를포함하고있음. 이진파일 (binary file) 모든데이터를컴퓨터내부의이진수표현그대로저장한파일로이미지파일이나실행파일등이이진파일임. 이진파일을이용하여메모리에저장된변수값을이진수표현그대로파일에저장할수도있음. 5
7.1 절 C 언어의파일입출력과정 1. 파일열기 fopen( ) 함수사용 2. 파일입출력 다양한파일입출력함수사용 3. 파일닫기 fclose( ) 함수사용 6
7.1 절 파일열기 파일을사용하기위해서는반드시 fopen() 을사용하여파일열기를해야함. 파일열기 fopen() 을하면열린파일에대한정보가 FILE 구조체형태로저장되고이 FILE 구조체에대한포인터가반환됨. FILE 구조체에대한포인터를간단히 FILE 포인터라함. 7
7.1 절 FILE 구조체 FILE 구조체안에는열린파일을위한여러필드변수들이선언되어있음. 예 버퍼에대한정보, 파일입출력모드, 파일디스크립터등을포함함. typedef struct { int cnt; unsigned char *base; unsigned char *ptr; unsinged flag; int fd; // 버퍼의남은문자수 // 버퍼시작 // 버퍼의현재포인터 // 파일입출력모드 // 열린파일디스크립터 FILE; // FILE 구조체 8
7.1 절 파일열기함수 fopen() 함수프로토타입 FILE *fopen(const char *filename, const char *mode); filename 열고자하는파일이름 mode 파일입출력모드 파일열기가성공하면 FILE 포인터를반환, 실패하면 NULL 을반환함. 텍스트파일에사용할수있는입출력모드 9
7.1 절 파일열기 파일을열기위해서는 FILE 포인터변수를미리선언하고 fopen() 함수를호출해야함. 사용예 1 FILE *fp; // FILE 포인터변수선언 fp = fopen( ~/sp/text.txt, r ); // 파일을읽기모드로염. if (fp == NULL) { printf(" 파일열기오류 \n"); 사용예 2 FILE *fp2; fp2 = fopen("outdata.txt", "w"); fp 가열린파일을나타냄. // 읽기모드인경우 // FILE 포인터변수선언 // 쓰기모드인경우 10
7.1 절 표준입출력스트림 스트림 (stream) fopen() 에의해서파일이열리면이를스트림이라고함. 표준입출력스트림 표준헤더파일 <stdio.h> 에는 3 개의스트림이정의되어있음. stdin, stdout, stderr C 프로그램이실행되면자동적으로열리고프로그램이종료될때자동으로닫히는스트림임. 모두 FILE 포인터임. 11
7.1 절 파일닫기 파일을열어서사용한후에더이상사용하지않으면 fclose() 를사용하여파일을닫아야함. fclose() 함수의프로토타입 int fclose(file *fp ); fp 사용예 FILE 포인터 파일닫기에성공하면 0 을반환하고, 실패하면 -1 을반환함. FILE *fp3; fp3 = fopen("name.txt", r"); // FILE 포인터변수선언 // 파일열기 fclose(fp3); // 파일닫기 12
7.2 절 텍스트파일 C 언어는텍스트파일에입출력하기위한함수들을제공함. 13
7.2 절 텍스트파일의문자단위입출력 텍스트파일은모든데이터를연속된문자형태로저장함. 문자단위입출력함수 파일입출력함수 fgetc(), getc() fputc(), putc() 기능 문자단위로입력하는함수 문자단위로출력하는함수 함수프로토타입 int fgetc(file *fp); fp 가가리키는파일에서한문자를읽어서반환함. 파일끝에도달했을경우에는 EOF 를반환함. int fputc(int c, FILE *fp); fp 가가리키는파일에한문자 (c) 를출력하고출력된문자를반환함. 출력시오류가발생하면 EOF 를반환함. 14
7.2 절 cat.c #include <stdio.h> int main(int argc, char *argv[]) { // 명령줄인수를받음. FILE *fp; int c; if (argc < 2) fp = stdin; else fp = fopen(argv[1],"r"); c = getc(fp); while (c!= EOF) { putc(c, stdout); c = getc(fp); fclose(fp); return 0; 15
7.2 절 copy.c #include <stdio.h> int main(int argc, char *argv[]) { char c; FILE *fp1, *fp2; if (argc!=3) { fprintf(stderr, " 사용법 : %s 파일 1 파일 2\n", argv[0]); return 1; fp1 = fopen(argv[1], "r"); if (fp1 == NULL) { fprintf(stderr, " 파일 %s 열기오류 \n", argv[1]); return 2; fp2 = fopen(argv[2], "w"); while ((c = fgetc(fp1))!= EOF) fputc(c, fp2); fclose(fp1); fclose(fp2); return 0; 16
7.2 절 기타파일관련함수 함수프로토타입 int feof(file *fp) fp 가가리키는파일의끝에도달하지않았으면 0 이아닌값을반환하고, 파일끝이면 0 을반환함. int ungetc(int c, FILE *p) c 에저장된문자를입력스트림에반환함. 마치문자를읽지않은것처럼파일위치포인터를 1 감소시킨위치에 c 를저장함. 17
7.2 절 명령줄인수 명령줄인수 (command line argument) 란커맨드라인에서프로그램을실행시킬때프로그램내부로전달하는인수를말함. 명령줄인수를사용하지않을때 main 함수형태 : int main(void) 프로그램실행 : 실행파일명 mycat 명령줄인수를사용할때 main 함수형태 : int main(int argc, char *argv[]) 프로그램실행 : 실행파일명인수 1 인수 2 mycat 1 2 mycat -w 18
7.2 절 명령줄인수예제 #include <stdio.h> cmdline.c int main(int argc, char *argv[]) { int i; printf("argc=%d\n", argc); for(i=0;i<argc;i++) printf("*argv[%d]=%s\n", i, argv[i]); return 0; 19
7.2 절 텍스트파일의줄단위입출력 텍스트파일은텍스트문자들을저장하는파일로여러개의줄로구성됨. 줄단위입출력함수 파일입출력함수 fgets() fputs() 기능 문자열을입력하는함수 문자열을출력하는함수 20
7.2 절 함수설명 함수프로토타입 char* fgets(char *s, int n, FILE *fp); fp 가가리키는파일로부터한줄을읽어서문자열포인터 s 에저장하고 s 를반환함. 개행문자 ('\n') 나 EOF 를만날때까지파일로부터최대 n-1 개의문자를읽고, 읽어온데이터의끝에는 NULL 문자를붙임. 오류가발생하면 NULL 포인터를반환함. int fputs(const char *s, FILE *fp); 문자열 s 를 fp 가가리키는파일에출력함. 문자열끝을나타내는 NULL 문자는출력하지않음. 성공하면출력한바이트수를반환하고, 실패하면 EOF 값을반환함. 21
7.2 절 line.c #include <stdio.h> #define MAXLINE 80 상수매크로선언 int main(int argc, char *argv[]) { FILE *fp; int line = 0; char buffer[maxline]; if (argc!= 2) { fprintf(stderr, " 사용법 : line 파일이름 \n"); return 1; if ( (fp = fopen(argv[1],"r")) == NULL){ fprintf(stderr, " 파일열기오류 \n"); return 2; while (fgets(buffer, MAXLINE, fp)!= NULL) { line++; printf("%3d %s", line, buffer); fclose(fp); return 0; 파일을닫음. 종료시킴. 종료코드 : 0 명령줄인수를받음. PPT 슬라이드 18~19쪽참고파일포인터변수 fp를선언함. 숫자저장용변수 line을 0으로초기화함. char형배열 buffer를선언함. 명령줄인수가 1개가아니면 모니터에문자열을출력하고종료시킴. 종료코드 : 1 종료시킴. 종료코드 : 2 텍스트파일을읽기모드로열고, 열기가실패했다면 모니터에문자열을출력하고 화면에문자열을출력함. 열린파일 fp 로부터한줄을읽어와배열 buffer 에저장함. 이때 buffer 의길이는 MAXLINE 임. 실패하지않았다면 line 변수값을 1 증가시키고, 변수 line 의숫자와배열 buffer 의문자열을화면에출력함. 22
7.2 절 텍스트파일의포맷입출력 텍스트파일에포맷 ( 서식 ) 을정해서입출력할수있음. 포맷입출력함수 파일입출력함수 fscanf() fprintf() 기능 자료형에따라자료를입력하는함수 자료형에따라자료를출력하는함수 23
7.2 절 포맷입출력함수설명 함수프로토타입 int fprintf(file *fp, const char *format,...); fp 는츨력할파일을가리키는 FILE 포인터임. 두번째부터의인자는 printf 함수와동일함. int fscanf(file *fp, const char *format,...); 사용예 fp 는입력받을파일을가리키는 FILE 포인터임. 두번째부터의인자는 scanf 함수와동일함. 읽은개수를반환함. fscanf(fp, %d, &i); fprintf(fp, %d, i); 24
7.2 절 fprint.c #include <stdio.h> #include "student.h" int main(int argc, char* argv[]) { struct student rec; 구조체변수 rec을선언함. FILE *fp; if (argc!= 2) { 명령줄인수가 1개가아니면 fprintf(stderr, " 사용법 : %s 파일이름 \n", argv[0]); return 1; 종료시킴. 종료코드 : 1 fp = fopen(argv[1], "w"); printf("%-9s %-7s %-4s\n", " 학번 ", " 이름 ", " 점수 "); while (scanf("%d %s %d", &rec.id, rec.name, &rec.score)==3) fprintf(fp, "%d %s %d ", rec.id, rec.name, rec.score); fclose(fp); return 0; 파일을닫음. 종료시킴. 종료코드 : 0 명령줄인수를받음. PPT 슬라이드 18~19 쪽참고. 파일포인터변수 fp 를선언함. 텍스트파일을쓰기모드로엶. student.h struct student { int id; char name[20]; int score; ; 모니터에문자열을출력하고 화면에문자열을출력함. 3 개가입력되었다면 열린파일 fp 에저장함. 빈칸주의 25
7.2 절 fscan.c #include <stdio.h> #include "student.h" int main(int argc, char* argv[]) { struct student rec; 구조체변수 rec을선언함. FILE *fp; if (argc!= 2) { 명령줄인수가 1개가아니면 fprintf(stderr, " 사용법 : %s 파일이름 \n", argv[0]); return 1; 종료시킴. 종료코드 : 1 fp = fopen(argv[1], "r"); 텍스트파일을읽기모드로엶. printf("%-9s %-7s %-4s\n", " 학번 ", " 이름 ", " 점수 "); while (fscanf(fp,"%d %s %d", &rec.id, rec.name, &rec.score)==3) printf("%10d %6s %6d\n", rec.id, rec.name, rec.score); fclose(fp); return 0; 파일을닫음. 종료시킴. 종료코드 : 0 명령줄인수를받음. PPT 슬라이드 18~19 쪽참고. 파일포인터변수 fp 를선언함. student.h struct student { int id; char name[20]; int score; ; 모니터에문자열을출력하고 화면에문자열을출력함. 열린파일 fp 로부터 3 개가입력되었다면 읽어온내용을화면에출력함. 26
7.3 절 이진파일 이진파일 (binary file) 은모든데이터를컴퓨터내부의이진수표현그대로저장한파일임. 이미지파일이나실행파일이대표적인이진파일임. 이진파일을이용하여메모리에저장된변수값을이진수표현그대로파일에저장할수도있음. 27
7.3 절 이진파일열기 이진파일로부터입출력을하기위해서는먼저이진파일을열어야함. 이진파일도 fopen() 함수를호출하여엶. fp = fopen( file, rb ); 입출력모드에 b 가붙고, 각모드의의미와동작은텍스트파일과같음. 이진파일에사용할수있는입출력모드 28
7.3 절 블록단위입출력 블록단위입출력이란한꺼번에일정한크기의연속된데이터 ( 블록 ) 를파일에서읽거나파일에쓰는것을말함. 예를들어, 파일에서한번에 100 바이트씩읽거나쓸수있음. 또는, 한번에하나의레코드 ( 변수 ) 크기만큼읽거나쓸수있음. 29
7.3 절 블록단위입출력함수 함수프로토타입 int fread(void *buf, int size, int n, FILE *fp); fp 가가리키는파일에서 size 크기의블록 ( 연속된바이트 ) 을 n 개읽어서, 포인터 buf 가가리키는곳에저장함. 읽어온블록의개수를반환함. int fwrite(const void *buf, int size, int n, FILE *fp); 사용예 fp 가지정한파일에 buf 에저장되어있는 size 크기의블록을 n 개기록함. 성공적으로출력한블록개수를반환함. int data1; fread(&data1, sizeof(data1), 1, fp1); double data2=3.5; fwrite(&data2, sizeof(data2), 1, fp2); 열린파일 fp1 에서 sizeof(int) 크기만큼의데이터를 1 개읽어와서변수 data1 에저장함. 변수 data2 에서 sizeof(double) 크기만큼의데이터를 1 개읽어와서열린파일 fp2 에저장함. 30
7.3 절 stcreate1. c #include <stdio.h> #include "student.h" int main(int argc, char* argv[]) { struct student rec; 구조체변수 rec을선언함. FILE *fp; if (argc!= 2) { fprintf(stderr, " 사용법 : %s 파일이름 \n",argv[0]); return 1; 종료시킴. 종료코드 : 1 fp = fopen(argv[1], "wb"); 이진파일을쓰기모드로엶. printf("%-9s %-7s %-4s\n", " 학번 ", " 이름 ", " 점수 "); while (scanf("%d %s %d", &rec.id, rec.name, &rec.score) == 3) fwrite(&rec, sizeof(rec), 1, fp); fclose(fp); return 0; 파일을닫음. 종료시킴. 종료코드 : 0 명령줄인수를받음. PPT 슬라이드 18~19 쪽참고. 파일포인터변수 fp 를선언함. 명령줄인수가 1 개가아니면 student.h struct student { int id; char name[20]; int score; ; 모니터에문자열을출력하고 화면에문자열을출력함. 3 개가입력되었다면 변수 rec 에서 sizeof(struct student) 크기만큼의데이터를 1 개읽어와서열린파일 fp 에저장함. 31
7.3 절 stprint.c #include <stdio.h> #include "student.h" int main(int argc, char* argv[]) { struct student rec; FILE *fp; if (argc!= 2) { 명령줄인수를받음. PPT 슬라이드 18~19 쪽참고. fprintf(stderr, " 사용법 : %s 파일이름 \n", argv[0]); return 1; if ((fp = fopen(argv[1], "rb")) == NULL ) { fprintf(stderr, " 파일열기오류 \n"); return 2; printf("-----------------------------------\n"); printf("%10s %6s %6s\n", " 학번 ", " 이름 ", " 점수 "); printf("-----------------------------------\n"); while (fread(&rec, sizeof(rec), 1, fp) > 0) if (rec.id!= 0) 구조체변수 rec 을선언함. 파일포인터변수 fp 를선언함. 명령줄인수가 1 개가아니면 블록이있다면 printf("%10d %6s %6d\n", rec.id, rec.name, rec.score); printf("-----------------------------------\n"); fclose(fp); return 0; 모니터에문자열을출력하고종료시킴. 종료코드 : 1 파일을열고, 열기가실패했다면모니터에문자열을출력하고종료시킴. 종료코드 : 2 화면에문자열을출력함. 열린파일 fp 에서 sizeof(struct student) 크기만큼의데이터를 1 개읽어와서변수 rec 에저장하고, 읽어온 파일에서읽어와저장한 rec 변수에 id 항목이 0 이아니라면, 문자열을출력함. (rec.id!= 0 문장은이예제에서는큰의미가없지만, 7.4 절임의접근에서중요한의미를갖음.) 32
7.4 절 임의접근의필요성 입출력함수들을호출하면데이터를읽거나쓰고, 읽거나쓴바이트수만큼 현재파일위치 가자동으로증가함. 파일의크기가매우크고원하는데이터가파일의중간중간에있을때순차접근 (sequential access) 방법은매우불편하고자료추출에많은시간을낭비함. 따라서사용자가원하는자료가있는곳을직접임의접근 (random access) 할수있는방법이필요함. 현재파일위치 (current file position, 열린파일에서다음으로읽거나쓸파일내의위치 ) 파일위치포인터 (file position pointer, 현재파일위치를가리키고있는포인터변수 ) 33
7.4 절 임의접근관련함수 함수프로토타입 fseek(file *fp, long offset, int mode) fp 가가리키는파일의현재파일위치를기준점 (mode) 을기준으로 offset 만큼이동시킴. rewind(file *fp) fp 가가리키는파일의현재파일위치를파일시작점으로이동시킴. ftell(file *fp) fp 가가리키는파일의현재파일위치를반환함. 34
7.4 절 fseek() 함수의사용예 다양한현재파일위치의이동 fseek(fd, 0L, SEEK_SET); 파일시작으로이동 (rewind) fseek(fd, 100L, SEEK_SET); 파일시작에서 100 바이트위치로이동 fseek(fd, 0L, SEEK_END); 파일끝으로이동 (append) 레코드 ( 변수 ) 단위의현재파일위치의이동 fseek(fd, n * sizeof(record), SEEK_SET); n+1 번째레코드시작위치로이동 fseek(fd, sizeof(record), SEEK_CUR); 다음레코드시작위치로이동 fseek(fd, -sizeof(record), SEEK_CUR); 전레코드시작위치로이동 파일끝이후로도이동가능 fseek(fd, sizeof(record), SEEK_END); 파일끝에서한레코드다음위치로이동. 이경우, 한레코드크기만큼의빈공간 (hole) 이만들어짐. 35
7.4 절 파일끝이후로의이동예 빈파일을쓰기모드로열고, 2 개의레코드 (record1, record2) 를저장한후, fseek 함수를이용해파일끝에서한레코드만큼다음위치로이동한후, 1 개의레코드 (record3) 를저장했을때 코드 fwrite(&record1, sizeof(record), 1, fp); fwrite(&record2, sizeof(record), 1, fp); fseek(fd, sizeof(record), SEEK_END); fwrite(&record3, sizeof(record), 1, fp); 그림 빈공간 (hole) 은 0 으로채워짐. 파일끝을지나서자유롭게현재파일위치를이동시킬수있는기능은레코드를계산된위치에저장하는데매우유용함. 36
7.4 절 순차접근의문제점 앞에서살펴본학생정보프로그램들은학생정보를입력된순서대로순차적으로저장하였음 프로그램 7.4 텍스트파일형태로저장함. 프로그램 7.6 이진파일형태로저장함. 특정학생을검색하기위해서는학생정보를처음부터하나씩읽으면서해당학생인지검사해야함. 최악의경우, 파일내의모든학생의정보를다읽어보아야함. 37
7.4 절 임의접근을이용한프로그램 프로그램 7.8 에서는블록입출력과임의접근기능을이용하여특정학생의정보를학번을이용하여바로검색할수있도록함. 학생레코드의파일내저장할위치를학번 (rec.id) 을이용하여시작학번 (START_ID) 에대한상대위치로결정함. (rec.id - START_ID)*sizeof(rec) #define START_ID 1401001 scanf("%d %s %d", &rec.id, rec.name, &rec.score); fseek(fp, (rec.id - START_ID) * sizeof(rec), SEEK_SET); fwrite(&rec, sizeof(rec), 1, fp); 빈공간 (hole) 은 0으로채워짐. 학번이 1401004인학생정보의저장위치학번이 1401003인학생정보의저장위치학번이 1401002인학생정보의저장위치 학번이 1401001 인학생정보의저장위치 38
7.4 절 stcreate.c (p.220) #include <stdio.h> #include "student.h" int main(int argc, char* argv[]) { struct student rec; 구조체변수 rec을선언함. FILE *fp; if (argc!= 2) { fprintf(stderr, " 사용법 : %s 파일이름 \n",argv[0]); return 1; 종료시킴. 종료코드 : 1 fp = fopen(argv[1], "wb"); 이진파일을쓰기모드로엶. printf("%-9s %-7s %-4s\n", " 학번 ", " 이름 ", " 점수 "); while (scanf("%d %s %d", &rec.id, rec.name, &rec.score) == 3) { fseek(fp, (rec.id - START_ID) * sizeof(rec), SEEK_SET); fwrite(&rec, sizeof(rec), 1, fp); fclose(fp); return 0; 파일을닫음. 종료시킴. 종료코드 : 0 명령줄인수를받음. PPT 슬라이드 18~19 쪽참고. 파일포인터변수 fp 를선언함. 명령줄인수가 1 개가아니면 student.h #define START_ID 1401001 struct student { int id; char name[20]; int score; ; 모니터에문자열을출력하고 화면에문자열을출력함. 3 개가입력되었다면 파일내저장할위치로이동함. 변수 rec 에서 sizeof(struct student) 크기만큼의데이터를 1 개읽어와서열린파일 fp 에저장함. 39
7.4 절 실행결과 40
7.4 절 파일읽기시주의할점 프로그램 7.8 에서블록입출력과임의접근기능을이용하여만든파일은중간중간에빈공간이있을수있음. 따라서파일에서레코드값을읽어온후, 그값이 0 이면 (rec.id == 0) 입력한적이없는데이터이고, 읽어온레코드값이 0 이아니면 (rec.id!= 0) 입력한적이있는데이터임을알수있음. 빈공간 (hole) 은 0 으로채워짐. 학번이 1401004인학생정보의저장위치학번이 1401003인학생정보의저장위치학번이 1401002인학생정보의저장위치 학번이 1401001 인학생정보의저장위치 41
7.4 절 #include <stdio.h> #include "student.h" int main(int argc, char* argv[]) { struct student rec; char c; int id; FILE *fp; if (argc!= 2) { stquery.c (p.222) #1 fprintf(stderr, " 사용법 : %s 파일이름 \n", argv[0]); return 1; 종료시킴. 종료코드 : 1 if ((fp = fopen(argv[1], "rb")) == NULL ) { fprintf(stderr, " 파일열기오류 \n"); return 2; 종료시킴. 종료코드 : 2 명령줄인수를받음. PPT 슬라이드 18~19 쪽참고. 구조체변수 rec 을선언함. Y 또는 N 를저장할변수 c 를선언함. 파일포인터변수 fp 를선언함. 명령줄인수가 1 개가아니면 student.h #define START_ID 1401001 struct student { int id; char name[20]; int score; ; 키보드로입력할학생학번을저장할변수 id 를선언함. 모니터에문자열을출력하고 텍스트파일을읽기모드로열고열기가실패했다면 모니터에문자열을출력하고 42
7.4 절 stquery.c (p.222) #2 do { printf(" 검색할학생의학번입력 : "); if (scanf("%d", &id) == 1) { fseek(fp, (id - START_ID) *sizeof(rec), SEEK_SET); if ((fread(&rec, sizeof(rec), 1, fp) > 0) && (rec.id!= 0)) printf(" 학번 : %8d 이름 : %4s 점수 : %4d\n", rec.id, rec.name, rec.score); else printf(" 레코드 %d 없음 \n", id); else printf(" 입력오류 "); 학번 1 개가입력되었다면 읽어올위치로이동함. 읽기가성공했고, 읽어온값중 rec.id 가 0 이아니라면 읽어온값을화면에출력함. printf(" 계속하겠습니까?(Y/N)"); scanf(" %c", &c); while (c == 'Y'); fclose(fp); return 0; 파일을닫음. 종료시킴. 종료코드 : 0 빈칸주의 키보드로문자하나를입력받아변수 c 에저장함. 읽기가실패했거나, 읽어온값중 rec.id 가 0 이라면, 문자를출력함. 학번 1 개가입력되지않았다면문자를출력함. c 의값이 Y 이면 do { 구간을다시실행하고, Y 가아니면밑으로빠져나옴. 43
7.4 절 실행결과 44
7.4 절 파일의특정레코드를수정할시주의할점 파일에저장된레코드를수정하는과정 1 파일로부터해당레코드를읽음. 2 이레코드를수정함. 3 수정된레코드를파일의원래위치에써야함. 2 1 3-1 3-2 ( 주의점 ) fread 함수로레코드를읽으면현재파일위치가다음레코드의시작위치로이동함. fread(&rec, sizeof(rec), 1, fp); 따라서수정된레코드를파일에쓰기전에반드시현재파일위치를원래위치로이동시켜주어야함. fseek(fp, -sizeof(rec), SEEK_CUR); fwrite(&rec, sizeof(rec), 1, fp); 45
7.4 절 #include <stdio.h> #include "student.h" int main(int argc, char* argv[]) { struct student rec; char c; int id; FILE *fp; if (argc!= 2) { stupdate.c (p.222) #1 fprintf(stderr, " 사용법 : %s 파일이름 \n", argv[0]); return 1; 종료시킴. 종료코드 : 1 if ((fp = fopen(argv[1], "rb+")) == NULL ) { fprintf(stderr, " 파일열기오류 \n"); return 2; 종료시킴. 종료코드 : 2 명령줄인수를받음. PPT 슬라이드 18~19 쪽참고. 구조체변수 rec 을선언함. Y 또는 N 를저장할변수 c 를선언함. 파일포인터변수 fp 를선언함. 명령줄인수가 1 개가아니면 student.h #define START_ID 1401001 struct student { int id; char name[20]; int score; ; 키보드로입력할학생학번을저장할변수 id 를선언함. 모니터에문자열을출력하고 텍스트파일을읽기쓰기모드로열고열기가실패했다면 모니터에문자열을출력하고 46
7.4 절 stupdate.c (p.222) #2 do { printf( 수정할학생의학번입력 : "); if (scanf("%d", &id) == 1) { fseek(fp, (id - START_ID) *sizeof(rec), SEEK_SET); if ((fread(&rec, sizeof(rec), 1, fp) > 0) && (rec.id!= 0)){ printf(" 학번 : %8d 이름 : %4s 점수 : %4d\n", rec.id, rec.name, rec.score); printf(" 새로운점수입력 : "); scanf("%d", &rec.score); fseek(fp, -sizeof(rec), SEEK_CUR); fwrite(&rec, sizeof(rec), 1, fp); else printf(" 레코드 %d 없음 \n", id); else printf(" 입력오류 "); printf(" 계속하겠습니까?(Y/N)"); scanf(" %c", &c); while (c == 'Y'); fclose(fp); return 0; 파일을닫음. 종료시킴. 종료코드 : 0 학번 1 개가입력되지않았다면문자를출력함. 빈칸주의키보드로문자하나를입력받아변수 c에저장함. 학번 1 개가입력되었다면 읽어올위치로이동함. 읽기가성공했고, 읽어온값중 rec.id 가 0 이아니라면 읽어온값을화면에출력함. 변수 rec 의점수 (score) 만키보드로새로입력받음. 현재파일위치를 rec 크기만큼앞으로이동시킴. 변수 rec 의값을파일에저장함. 읽기가실패했거나, 읽어온값중 rec.id 가 0 이라면, 문자를출력함. c 의값이 Y 이면 do { 구간을다시실행하고, Y 가아니면밑으로빠져나옴. 47
7.4 절 실행결과 48
질문 Q&A 49
7.5 절 버퍼입출력 C 표준입출력라이브러리에서는입출력을최적화하기위해버퍼 (buffer) 를할당하여사용함. 버퍼를사용함으로써실제디스크에입출력하는횟수를줄일수있음. fputc(fp, c) 버퍼 버퍼가다차면버퍼에저장된내용을모두디스크에저장함. 디스크 50
7.5 절 버퍼방식 완전버퍼방식 버퍼가꽉찼을때실제디스크입출력을수행하는방식 주로파일입출력에사용됨. 줄버퍼방식 개행문자 (new line, \n ) 를만나면실제입출력을수행하는방식 표준입출력 (stdin과 stdout) 이대표적인줄버퍼방식임. 버퍼미사용방식 입출력에버퍼를사용하지않는방식 표준에러 (stderr) 가대표적인버퍼미사용방식임. 51
7.5 절 buffer.c (p.229) #1 #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { FILE *fp; if (!strcmp(argv[1], "stdin")) { fp = stdin; printf(" 한글자입력 :"); if (getchar() == EOF) perror("getchar"); else if (!strcmp(argv[1], "stdout")) fp = stdout; else if (!strcmp(argv[1], "stderr")) fp = stderr; else if ((fp = fopen(argv[1], "r")) == NULL) { perror("fopen"); exit(1); else if (getc(fp) == EOF) perror("getc"); 빈파일일때 52
7.5 절 buffer.c (p.229) #2 printf(" 스트림 = %s, ", argv[1]); if (fp->_flags & _IO_UNBUFFERED) printf(" 버퍼미사용 "); else if (fp->_flags & _IO_LINE_BUF) printf(" 줄버퍼사용 "); else printf(" 완전버퍼사용 "); printf(", 버퍼크기 = %d\n", fp->_io_buf_end - fp->_io_buf_base); exit(0); 53
7.5 절 실행결과 54
7.5 절 버퍼사용의 on/off setbuf() 함수를이용하여버퍼사용을 on/off 할수있음. 함수프로토타입 void setbuf(file *fp, char *buf); 사용예 fp는열린파일 ( 스트림 ) 을나타냄. buf는길이가 BUFSIZ(8192) 인배열의주소를나타냄. setbuf(fp, NULL); fp 의버퍼사용 off char buf[bufsiz]; setbuf(stdout, buf); stdout 의버퍼를 buf 로지정 55
7.5 절 setbuf.c (p.231) #include <stdio.h> main() { printf(" 안녕하세요, "); sleep(1); printf(" 리눅스입니다!"); sleep(1); printf(" \n"); sleep(1); setbuf(stdout, NULL); printf(" 여러분, "); sleep(1); printf(" 반갑습니다 "); sleep(1); printf(" ^^"); sleep(1); printf(" \n"); sleep(1); 버퍼미사용으로전환 56
7.5 절 실행결과 57
7.5 절 버퍼의다양한설정 setvbuf() 함수를이용하여버퍼의사용방식과버퍼의크기를구체적으로설정할수있음. 함수프로토타입 int setvbuf(file *fp, char *buf, int mode, size_t size); 사용예 fp 는열린파일 ( 스트림 ) 을나타냄. buf 는버퍼로사용할배열의주소를나타냄. mode 는버퍼사용방식을나타냄. _IOFBF: 완전버퍼, _IOLBF: 줄버퍼, _IONBF: 버퍼미사용 size 는버퍼의크기를나타냄. setvbuf(fp, NULL, _IONBF, 0); fp 의버퍼사용 off, 즉, 버퍼미사용 char buf[512]; setbuf(fp, buf, _IOLBF, 512); fp 의버퍼로 buf 배열을이용하고줄버퍼방식을사용함. 58
7.5 절 setvbuf.c (p.232) #include <stdio.h> int main( void ) { char buf[1024]; FILE *fp1, *fp2; fp1 = fopen("data1", "a"); fp2 = fopen("data2", "w"); if(setvbuf(fp1, buf, _IOFBF, sizeof(buf))!= 0) printf(" 첫번째스트림 : 잘못된버퍼 \n" ); else printf(" 첫번째스트림 : 1024 바이트크기버퍼사용 \n" ); setvbuf() 함수는성공하면 0 을반환함. if(setvbuf(fp2, NULL, _IONBF, 0)!= 0) printf(" 두번째스트림 : 잘못된버퍼 \n" ); else printf(" 두번째스트림 : 버퍼미사용 \n" ); setvbuf() 함수는성공하면 0 을반환함. 59
7.5 절 실행결과 60
7.5 절 기타버퍼관련함수 fflush() 함수는스트림의버퍼를비움. 함수프로토타입 int fflush(file *fp); fp의버퍼가출력버퍼이면, 버퍼에남아있는데이터를모두씀. fp의버퍼가입력버퍼 (stdin) 이면, 버퍼를비움. fp가 NULL이면모든열린파일들의버퍼를비움. 61
7.6 절 기타함수 C 표준라이브러리는문자및문자열처리와관련된다양한함수들을제공함. 교재 234 쪽 ~ 236 쪽주요문자및문자열처리함수목록을읽고다양하게응용해보세요. 62
질문 Q&A 63