490 likes | 1.2k Views
제 16 장 파일 입출력. 쉽게 풀어쓴 C 언어 Express. C Express. 이번 장에서 학습할 내용. 입출력에 관련된 개념들과 함수들에 대하여 학습한다. 입출력 형식 스트림의 개념 파일 입출력 입출력 관련 함수. 출력 형식 지정자. 출력 형식 지정자 (format specification) 변환 지정자 (conversion specification). 문자 , 정수 , 실수 등 출력 타입 명시 ( 변환 문자 ). 출력 필드의 크기 명시.
E N D
제16장 파일 입출력 쉽게 풀어쓴 C언어 Express C Express
이번 장에서 학습할 내용 입출력에 관련된 개념들과 함수들에 대하여 학습한다. • 입출력 형식 • 스트림의 개념 • 파일 입출력 • 입출력 관련 함수
출력 형식 지정자 • 출력 형식 지정자(format specification) • 변환 지정자(conversion specification) • 문자, 정수, 실수 등출력 타입 명시(변환 문자) • 출력 필드의크기 명시 %[플래그][필드폭][.정밀도]형식문자 • 정렬 방식,부호 출력, 공백 문자 출력, 소수점, 8/16진수 접두사 출력 등 명시 • 소수점 이하 자릿수 명시
출력 형식 지정자 123.456000 1.23e+002 1.230000e+001 1.230000E+001
입력 형식 • 8진수, 16진수 입력 • 필드폭을 지정하여 입력 #include <stdio.h>int main( ) {int a, b;printf("5자리 정수 입력: ");scanf("%3d%3d", &a, &b);// 최대 3문자씩 입력printf("a = %d, b = %d\n", a, b);return0;} #include <stdio.h>int main() { int d, o, x; sscanf("10 10 10", "%d%o%x", &d, &o, &x); printf("d = %d, o = %d, x = %d\n", d, o, x);return0;} d = 10, o = 8, x = 16 5자리 정수 입력: 12345 a = 123, b = 45
문자와 문자열 입력 scan set(문자집합)
예제: 문자열 입력 #include<stdio.h> int main(void) { charc, s[80], t[80]; printf("공백으로 분리된문자열입력: "); scanf("%s%c%s", s, &c, t); printf("첫번째문자열=%s\n", s); printf("중간 공백 문자=%c\n", c); printf("두번째문자열=%s\n", t); return 0; } 공백으로 분리된 문자열 입력: Hello World 첫번째문자열=Hello 중간 공백 문자= 두번째문자열=World
예제: 문자 집합 입력 #include<stdio.h> int main(void) { char s[80]; printf("문자열입력: "); scanf("%[abc]", s); printf("입력된문자열 = %s\n", s); return 0; } 문자열 입력:abacadae 입력된 문자열= abaca
예제: scanf반환 값 #include<stdio.h> int main(void) { int x, y, z; if (scanf("%d %d %d", &x, &y, &z) == 3) printf("정수들의 합 =%d\n", x + y + z); else printf("입력 오류\n"); return 0; } 10 20 30 정수들의 합 =60 10 20 a 입력 오류
scanf사용시 주의점 • 입력 값을 저장할 변수의 주소 전달 • inti; scanf("%d", &i); // OK • scanf("%d", i); // ERROR • 배열의 이름은 배열을 가리키는 포인터 • charstr[80]; scanf("%s", str); // OK • scanf("%s", &str); // ERROR • 충분한 공간 확보 • charstr[80]; • scanf("%s", str);// 입력된 문자가 80개 이상이면 오류 • 형식 문자열 끝에 '\n'을 사용하지 말 것 • inti; • scanf("%d\n", &i); scanf("%d\n", &i); printf("%d\n", i); 12 ↵ ↵ ↵ ↵ 3 12
스트림의 개념 • 스트림(stream): 입력과 출력을 바이트의 흐름으로 생각하는 것
스트림과 버퍼 • 스트림에는일반적으로 버퍼(buffer)가 사용됨
FILE과 표준 스트림 • 스트림은C에서 FILE 구조체로 표현 • typedefstruct { … } FILE; // <stdio.h> • 표준 스트림(standard stream)은 자동으로 생성됨 • FILE *stdin, *stdout, *stderr; // <stdio.h>
입출력 함수의 분류 • 사용하는 스트림에 따른 분류 • 표준 입출력 스트림을 사용하는 함수 • 스트림을 구체적으로 명시해야 하는 함수 • 입출력 형식 사용 여부에 따른 분류 • 입출력 형식을 사용하지 않는 함수: unformatted I/O • 입출력 형식을 사용하는 함수: formatted I/O
파일 • 파일에 저장된 데이터는 프로그램 실행이 끝나도 보존됨 • C에서 파일은 일련의 연속된 바이트 • 모든 파일 데이터는 결국 바이트들로 파일에 저장 • 파일 데이터에 대한 해석은 프로그래머에게 달려 있음 • 예: 파일의 4개 바이트 int, float, 픽셀, … 하나의 정수? 하나의 실수? 4개의 문자? 0x36 0x34 0x31 0x0
텍스트 파일(text file) • 사람이 읽을 수 있는 텍스트가 들어 있는 파일 • 예: C 프로그램 소스 파일, 메모장 파일 • 아스키 코드, 한글 코드, 유니코드 등을 사용하여 저장 • 라인들로 구분됨: 운영체제에 따라 라인 끝 표지가 다름 윈도우, MS_DOS ‘W’ ‘O’ ‘R’ ‘L’ ‘D’ ‘\n’ ‘\n’ ‘W’ ‘O’ ‘R’ ‘L’ ‘D’ ‘\r’ 유닉스, 리눅스 C언어 ‘W’ ‘O’ ‘R’ ‘L’ ‘D’ ‘\n’ ‘W’ ‘O’ ‘R’ ‘L’ ‘D’ ‘\r’ 매킨토시
이진 파일(binary file) • 사람이 직접 읽을 수는 없고 컴퓨터가 처리하기 위한 파일 • 예: C 프로그램 실행 파일, 사운드 파일, 이미지 파일 • 이진 데이터를 직접 저장 • 문자열로 변환하지 않고 입출력 • 라인들로 구분되지 않음
파일 처리 개요 • 파일 처리 순서 • FILE 구조체를 이용 • FILE *: 파일 포인터(file pointer) 파일 읽기/쓰기 (read/write) 파일 열기 (open) 파일 닫기 (close)
파일 열기와 닫기 FILE *fopen(const char *name, const char *mode); • 파일 열기 함수 • name: 파일 이름 • mode: 파일 모드(file mode) • 열기 실패의 경우 NULL 포인터 반환 intfclose(FILE *stream); • 파일 닫기 함수 • 반환 값: 성공 0, 실패 EOF FILE *fp; fp = fopen("test.txt", "r"); if (fp == NULL) // if(!fp) printf("test.txt: open fail\n"), exit(1); … fclose(fp); FILE *fp; if (!(fp = fopen("test.txt", "r"))) printf("test.txt: open fail\n"), exit(1); … fclose(fp);
파일 모드 • 이진 파일이면 끝에 b추가"rb", "wb", "ab", "r+b", "w+b", "a+b" • 텍스트 파일이면 끝에 t추가 또는 생략"r", "w", "a", "r+", "w+", "a+", "rt", "wt", "at", "r+t", "w+t", "a+t" • 수정 모드에서 읽기쓰기, 쓰기읽기 전환 시,반드시 fflush(), fseek(), fsetpos(), rewind() 중 하나를 먼저 호출해야 함
파일 삭제 int remove(const char *path); • 파일 삭제 함수 • 반환 값: 성공 0, 실패 0이 아닌 값 • 파일 포인터를 사용하지 않는 함수 if (remove("test.txt")) printf("test.txt: 삭제 실패\n");
파일 입출력 함수 크게 텍스트 입출력과이진 데이터 입출력으로 구분할 수 있습니다.
문자 입출력 intfgetc(FILE *fp); intgetc(FILE *fp); • 문자 입력 • fgetc함수; getc일반적으로 매크로 • 반환 값: 성공 입력된 문자, 실패(파일 끝, 에러) EOF • #define getchar( ) getc(stdin) intfputc(intc, FILE *fp); intputc(intc, FILE *fp); • 문자 출력 • fputc함수; putc일반적으로 매크로 • 반환 값: 성공 출력된 문자(c), 실패(에러) EOF • #define putchar(c) putc((c), stdout)
문자 입출력 #include<stdio.h> int main(void) { FILE *fp; fp = fopen("sample.txt", "w"); if (fp== NULL) returnprintf("파일 열기 실패\n"), 1; else printf("파일 열기 성공\n"); putc('a', fp); putc('b', fp); putc('c', fp); // fputc('a', fp); fputc('b', fp); fputc('c', fp); fclose(fp); return 0; } sample.txt abc 파일 열기 성공
문자 입출력 #include<stdio.h> int main(void) { FILE *fp; intc; if (!(fp= fopen("sample.txt", "r"))) returnprintf("파일 열기 실패\n"), 1; while ((c = getc(fp)) != EOF) // c = fgetc(fp) putchar(c); fclose(fp); return0; } sample.txt abc abc
문자열 입출력 char *fgets(char*s, int n, FILE *fp); • s에 한 라인의 문자열 입력 • 최대 n-1 문자만 입력 안전함, 공백 문자들도 입력, '\n'도 저장 • 반환 값: 성공 s, 실패(파일 끝, 에러) NULL • char *gets(char *s): stdin에서 입력 • 무조건 한 라인 입력 위험함 • '\n'을읽어들이지만 저장하지는 않음 intfputs(constchar*s, FILE *fp); • 문자열 s 출력 • 반환 값: 성공 음수가 아닌 값, 실패(에러) EOF • intputs(const char *s): stdout에 출력 • s를 출력한 후 '\n' 출력
문자열 입출력 char s[100]; fputs(fgets(s, 6, stdin), stdout); s ab cd\0 ab cde\n ab cd char s[100]; fputs(fgets(s, 10, stdin), stdout); char s[100]; puts(gets(s)); s s ab cde\n\0 ab cde\0 ab cde\n ab cde\n ab cde\n ab cde\n
문자열 입출력 #include<stdio.h> #include<stdlib.h> int main(void) { FILE *fp1, *fp2; char file1[100], file2[100]; char buffer[100]; printf("원본파일: "); scanf("%s", file1); printf("복사파일: "); scanf("%s", file2); if (!(fp1 = fopen(file1, "r"))) { fprintf(stderr,"%s: 열기 실패\n", file1); exit(1); } if (!(fp2 = fopen(file2, "w"))) { fprintf(stderr,"%s: 열기 실패\n", file2); exit(1); } while (fgets(buffer, 100, fp1) != NULL) fputs(buffer, fp2); fclose(fp1); fclose(fp2); return 0; } 원본 파일:a.txt 복사 파일:b.txt a.txt C 프로그래밍 C Programming b.txt C 프로그래밍 C Programming
문자열 입출력 proverb.txt #include<stdio.h> #include<stdlib.h> #include<string.h> intmain(void) { char *fname = "proverb.txt"; FILE *fp; intline_num = 0; char line[256], word[256]; printf("탐색할단어: "); scanf("%s", word); if (!(fp = fopen(fname, "r"))) fprintf(stderr,"%s: 열기 실패\n", fname), exit(1); while (fgets(line, 256, fp)) { line_num++; if (strstr(line, word)) printf("%s:%d -- %s 발견\n", fname, line_num, word); } fclose(fp); return 0; } A house divided against itself cannot stand. A good man is hard to find. A house is not a home. A friend in need is a friend indeed. 탐색할 단어: house proverb.txt:1 -- house 발견 proverb.txt:3 -- house 발견
형식화된 입출력 intfscanf(FILE *fp, const char *format, ...); intfprintf(FILE *fp, const char *format, ...); • printf("...", …): fprintf(stdout, "...", …) • scanf("...", …): fscanf(stdin, "...", …) FILE *fp; inti = 123; float f = 1.23F; if ((fp= fopen("sample.txt", "w"))) { fprintf(fp, "%d %.2f\n", i, f); fclose(fp); } FILE *fp; inti; floatf; if ((fp= fopen("sample.txt", "r"))) { fscanf(fp, "%d %f", &i, &f); printf("%d %.2f", i, f); fclose(fp); } sample.txt 123 1.23 123 1.23
예제 #include<stdio.h> typedefstruct { int number; char name[20]; float score; } Student; intmain(void) { FILE *fp; charfname[100]; intcount = 0; floattotal = 0.0F; Student s; printf("성적파일: "); scanf("%s", fname); if (!(fp = fopen(fname, "w"))) returnfprintf(stderr,"%s: 열기 실패\n", fname), 1; while (1) { printf("학번, 이름, 성적입력: "); if (scanf("%d %s %f", &s.number, s.name, &s.score) != 3) break; fprintf(fp, "%d %s %g\n", s.number, s.name, s.score); } fclose(fp); if (!(fp = fopen(fname, "r"))) returnfprintf(stderr,"%s: 열기 실패\n", fname), 1; while (fscanf(fp, "%d %s %f", &s.number, s.name, &s.score) == 3) total += s.score, count++; printf("평균 = %g\n", total / count); fclose(fp); return 0; } 성적 파일: score.txt 학번, 이름, 성적 입력: 1 KIM 90.2 학번, 이름, 성적 입력: 2 PARK 30.5 학번, 이름, 성적 입력: 3 MIN 56.8 학번, 이름, 성적 입력: ^Z 평균 = 59.1667 1 KIM 90.2 2 PARK 30.5 3 MIN 56.8 score.txt
버퍼링(buffering) • fopen()을 사용하여 파일을 열면자동으로 버퍼(buffer) 할당 • 버퍼는 입출력 데이터의 임시 저장 장소로 이용되는 메모리 블록 • 디스크 장치는 블록 단위로 입출력할 경우 효율적으로 동작 • 블록의 크기는 일반적으로 512바이트의 배수 • 예: 512, 1024, 4096, 8192 • flush: 버퍼의 내용을 비우는 동작 • fclose(): flush 버퍼 반납 • buffered I/O: 버퍼를 이용하는 입출력 물리적 입출력(Physical I/O) 논리적 입출력(Logical I/O) Buffer Program File
fflush, feof,ferror intfflush(FILE *fp); • 출력 스트림의 경우,버퍼의 내용을 실제 파일에 저장하고 버퍼를 비움 • 입력 스트림에 대한 동작은 OS에 따라 다름 • 반환 값: 성공 0, 실패EOF intfeof(FILE *fp); • 현재 파일 끝 상태이면 참(0이 아닌 값) 반환 intferror(FILE *fp); • 현재 파일이 에러 상태이면 참(0이 아닌 값) 반환
이진 데이터파일 • 메모리의 내부 데이터 표현 형식 그대로 저장
이진 데이터 출력 size_tfwrite(const void*buffer, size_tsize, size_tcount, FILE *fp); • buffer에서 크기가 size인 항목을 count 개수만큼 출력 • 반환 값: 출력된 항목 개수 • 반환 값이 count가 아니면 에러 FILE *fp; int a[ ] = { 10, 20, 30, 40, 50 }; size_t size = sizeof a[0]; size_t n = sizeof a / sizeof a[0]; if ((fp = fopen("data.bin", "wb"))) { if (fwrite(a, size, n, fp) != n) fprintf(stderr, "출력 실패\n"); fclose(fp); } int a[5] = { 10, 20, 30, 40, 50 }; if (fwrite(a, sizeof a, 1, fp) != 1) // error if (fwrite(a, sizeof a[0], 5, fp) != 5) // error
이진 데이터 입력 size_tfread(void*buffer, size_tsize, size_tcount, FILE *fp); • buffer에 크기가 size인 항목을 최대 count 개수만큼 입력 • 반환 값: 입력된 항목 개수 • 반환 값이 count보다 작으면 파일 끝이나 에러 #define SIZE 100 FILE *fp; int a[SIZE]; size_t n; if ((fp = fopen("data.bin", "rb"))) { n =fread(a, sizeof a[0], SIZE, fp); if (n < SIZE) fprintf(stderr, "파일 끝 또는 입력 실패\n"); fclose(fp); }
예제: 파일 복사 while ((n = fread(buf, 1, BUF_SIZE, fpi)) > 0)if (fwrite(buf, 1, n, fpo) != n) break;if (ferror(fpi))fprintf(stderr, "%s: fail to read\n", argv[1]), exit(1);if (ferror(fpo))fprintf(stderr, "%s: fail to write\n", argv[2]), exit(1);fprintf(stderr, "copied: %s => %s\n", argv[1], argv[2]); fclose(fpi); fclose(fpo);return0;} #include <stdio.h>#include <stdlib.h>#define USAGE "Usage: fcopy from to\n"#define BUF_SIZE 1024int main(intargc, char *argv[ ]) {FILE *fpi, *fpo;charbuf[BUF_SIZE];size_t n;if (argc != 3)fprintf(stderr, "%s", USAGE), exit(1); if (!(fpi = fopen(argv[1], "rb")))fprintf(stderr, "%s: fail to open\n", argv[1]), exit(1);if (!(fpo = fopen(argv[2], "wb")))fprintf(stderr, "%s: fail to open\n", argv[2]), exit(1); C> fcopy fcopy.exe fcopy2.exe copied: fcopy.exe => fcopy2.exe
예제: 파일 추가 // 첫 번째 파일을 두 번째 파일 끝에 추가 #include<stdio.h> #include<stdlib.h> #define BUF_SIZE 1024 intmain(void) { FILE *fp1, *fp2; charfile1[100], file2[100]; charbuffer[BUF_SIZE]; intn; printf("입력 파일: "); scanf("%s", file1); printf("추가할 파일: "); scanf("%s", file2); if (!(fp1 = fopen(file1, "rb"))) fprintf(stderr,"입력 파일 열기실패\n"), exit(1); if (!(fp2 = fopen(file2, "ab"))) fprintf(stderr,"추가할파일 열기실패\n"), exit(1); while ((n = fread(buffer, 1, BUF_SIZE, fp1)) > 0) fwrite(buffer, 1, n, fp2); fclose(fp1); fclose(fp2); return0; } 입력 파일: a.dat 추가할 파일: b.dat
예제: 이진 데이터 입출력 #include<stdio.h> #include<stdlib.h> #define SIZE 3 typedefstruct { int number; char name[20]; doublegpa; } Student; int main(void) { Student table[SIZE] = { { 1, "Kim", 3.99 }, { 2, "Min", 2.68 }, { 3, "Lee", 4.01 } }; Student s; FILE *fp; inti; if (!(fp = fopen("student.dat", "wb"))) fprintf(stderr,"출력파일 열기실패\n"), exit(1); if (fwrite(table, sizeof(Student), SIZE, fp) != SIZE) fprintf(stderr,"파일 출력실패\n"), exit(1); fclose(fp); if (!(fp = fopen("student.dat", "rb"))) fprintf(stderr,"입력파일 열기실패\n"), exit(1); for (i = 0; i< SIZE; i++) { if (fread(&s, sizeof s, 1, fp) != 1) fprintf(stderr,"파일 입력 실패\n"), exit(1); printf("%d, %s, %g\n", s.number, s.name, s.gpa); } fclose(fp); return0; } 1, Kim, 3.99 2, Min, 2.68 3, Lee, 4.01
예제: Hexa Dump // 16진수로 파일 내용 출력 #include<stdio.h> #include<stdlib.h> #include<ctype.h> intmain(void) { FILE *fp; charfname[100]; unsignedcharbuffer[16]; intaddress = 0; inti, bytes; printf("파일 이름: "); scanf("%s", fname); if (!(fp = fopen(fname, "rb"))) fprintf(stderr,"%s: 열기 실패\n", fname), exit(1); while ((bytes = fread(buffer, 1, 16, fp)) > 0) { printf("%08X: ", address); address += bytes; for (i = 0; i < bytes; i++) printf("%02X ", buffer[i]); for ( ; i < 16; i++) printf(" "); for (i = 0; i < bytes; i++) putchar(isprint(buffer[i]) ? buffer[i] : '.'); putchar('\n'); } fclose(fp); return0; } in.html <html> <body> <pre> <h1>Build Log</h1> 파일 이름: in.html 00000000: 3C 68 74 6D 6C 3E 0D 0A 3C 62 6F 64 79 3E 0D 0A <html>..<body>.. 00000010: 3C 70 72 65 3E 0D 0A 3C 68 31 3E 42 75 69 6C 64 <pre>..<h1>Build 00000020: 20 4C 6F 67 3C 2F 68 31 3E 0D 0A Log</h1>..
임의 접근 • 순차 접근(sequential access): 파일의 처음부터 차례대로 입출력 • 임의 접근(random access): 파일의 임의 위치에서 직접 입출력 직접 접근(direct access) • 파일 위치(file position) 표시자 • 바이트 단위로 현재 입출력 위치 정보 유지 • 첫 번째 바이트: 0번째 바이트 • 파일 위치를 이동시키면 임의 접근 가능 순차 접근 임의 접근 파일 위치 표시자 0 1 2 3 4 5 6 7
임의 접근 관련 함수 intfseek(FILE *fp, long offset, int origin); • origin기준으로 offset만큼 떨어진 바이트로 파일 위치 변경 • offset이 음수:파일 시작 쪽으로 이동 • offset이 양수:파일 끝 쪽으로 이동 • 반환 값: 성공 0, 실패 0이 아닌 값 fseek(fp, 0L, SEEK_SET); // 파일 시작으로 이동 (SEEK_SET이면 offset은 절대 위치) fseek(fp, 100L, SEEK_SET); // 100번째 바이트로 이동 fseek(fp, 0L, SEEK_END); // 파일 끝으로 이동 fseek(fp, -20L, SEEK_END); // 파일 끝에서 시작 쪽으로 20바이트 이동 fseek(fp, 50L, SEEK_CUR); // 현재 위치에서 끝 쪽으로 50바이트 이동 fseek(fp, sizeof(struct element), SEEK_CUR); // 구조체 크기만큼 파일 위치 증가 long ftell(FILE *fp); void rewind(FILE *fp); • 현재 파일 위치 반환 • 에러 -1 반환 • 파일 위치를 0으로 변경 • 파일 시작 위치로 이동
예제 #include<stdio.h> #include<stdlib.h> #define SIZE 1000 intmain(void) { FILE *fp; int table[SIZE], data, n; long pos; for (n = 0; n < SIZE; n++) table[n] = n * n; fp= fopen("sample.dat", "wb"); fwrite(table, sizeof table, 1, fp); fclose(fp); fp = fopen("sample.dat", "rb"); while (1) { printf("데이터 위치(0 ~ %d): ", SIZE - 1); scanf("%d", &n); if (n < 0 || n >= SIZE) break; pos = (long)(n * sizeof(int)); if (fseek(fp, pos, SEEK_SET)) fprintf(stderr, "fseek실패\n"), exit(1); fread(&data, sizeof(int), 1, fp); printf("%d: %d\n", n, data); } fclose(fp); return 0; } 데이터 위치(0 ~ 999): 3 3: 9 데이터 위치(0 ~ 999): 9 9: 81 데이터 위치(0 ~ 999): -1