스트림과 파일 입출력
-
스트림 : 모든 입력과 출력을 바이트들의 흐름으로 생각하는 것
-
스트림의 장점 - 장치 독립성(입출력 장치에 상관없이 프로그램을 작성)
-
스트림의 특징 - 버퍼를 사용(버퍼에 데이터 저장, 전송)
-
표준 입출력 스트림
1) stdin 표준 입력 스트림 scanf()
2) stdout 표준 출력 스트림 printf()
3) stderr 표준 오류 스트림 fprintf(stderr) -
입출력 함수
1) 형식이 없는 입출력(문자 형태)표준 : 일반 getchar() : fgetc(File *f) putchar() : fputc(File *f) gets() : fgets(File *f) puts() : fputs(File *f)
2) 형식이 있는 입출력(정수, 실수)
표준 : 일반 printf() : fprintf(File *f) scanf() : fscanf(File *f)
-
C에서의 모든 입력과 출력은 스트림 형식으로 처리된다.
-
스트림은 모든 입력과 출력을 바이트들의 흐름으로 간주한다.
-
스트림의 최대 장점은 장치 독립성이다.
-
입력을 위한 표준적인 스트림은 stdin이고 기본적으로 키보드 장치와 연결된다.
-
출력을 위한 표준적인 스트림은 stdout이고 기본적으로 콘솔화면 장치와 연결된다.
- printf(형식 제어 문자열)
- printf(%[플래그] [필드폭] [정밀도] 형식)
- 플래그(flag) : 하나의 문자로서 출력의 정렬과 부호 출력, 공백 출력, 8진수와 16진수 접두사 출력 등을 지시한다.
- ‘-’ : 왼쪽 정렬, 기본 : 오른쪽 정렬
- ‘+’ : 결과값을 출력할 때 항상 +와 -의 부호를 붙임
- ‘0’ : 출력값 앞에 공백 문자 대신에 0으로 채움
- ‘ ’ : 출력값 앞에 양수나 영인 경우 부호 대신 공백을 출력
- ‘#’ : 8진수 출력일 때는 출력값 앞에 0, 16진수는 출력값 앞에 0x
-
printf()에서 변수나 수식의 값을 출력하는 형식을 지정하는 문자열은 형식제어 문자열이다.
-
printf()에서 정렬을 구체적으로 지시하지 않으면 기본적으로 오른쪽 정렬된다.
-
형식 지정자 %d와 %i은 부호 있는 정수를 10진수 형태로 출력하는데 사용된다.
-
부호 없는 8진수, 10진수, 16진수 정수를 출력하는 사용되는 형식 지정자는 %o, %d, %x이다.
-
실수를 지수 표기법으로 출력하는데 사용되는 형식 지정자는 %e이다.
-
정수를 필드폭 6으로 출력하려면 %6d로 하여야 한다.
-
실수를 필드폭 10이고 소수점 이하 자릿수를 6자리로 출력하려면 %10.6f로 하여야 한다.
-
출력값을 왼쪽 정렬시키는 플래그는 -이다.
-
실수 출력의 경우, 정밀도를 지정하지 않으면 소수점 이하 자릿수는 기본적으로 6개가 된다.
- scanf()를 이용한 입력
- 문자들을 모아서 정수나 실수로 변환한다.
-
필드폭이 5가 지정되면 2개의 숫자와 3개의 숫자로 읽어질 수도 있다.
ex) 6개의 숫자로 이루어진 정수 입력 -> scanf(“%3d%3d”, &a, &b); - %c : char형으로 입력받는다.
- %s : 공백 문자가 아닌 문자부터 공백 문자가 나올 때까지를 문자열로 변환하여 입력받는다.
- %[abc] : 대괄호 안에 있는 문자 a,b,c로만 이루어진 문자열을 읽어들인다(첫 번째 문자가 문자 집합에 없으면 문자 배열에 아무것도 저장 안됨)
- %[^abc] : 대괄호 안에 있는 문자 a,b,c만을 제외하고 다른 문자들로 이루어진 문자열을 읽어 들인다.
- %[0-9] : 0에서 9까지의 범위에 있는 문자들로 이루어진 문자열을 읽어들인다.
-fflush(stdin) : 버퍼에 남아있는 데이터를 비움
-
scanf()에서 double 값을 입력받을 때 사용하는 형식 지정자는 %lf이다.
-
여러 개의 입력을 받는 경우, scanf()는 공백문자를 이용하여 각각의 입력을 분리한다.
- 파일의 기초
- 변수, 배열, 구조체 등은 모두 메모리로 만들어지고 이것들은 전원이 꺼지면 사라진다. -> 휘발성
- 하드 디스크에 파일 형태로 저장하면 전원이 꺼지더라도 데이터가 보존된다. -> 비휘발성
- 파일도 일련의 연속된 바이트이다.
- 모든 파일은 입출력 동작이 발생하는 현재 위치를 나타내는 파일 포인터를 가지고 있다.
- 텍스트 파일에서는 문자열의 끝을 나타내는데 NULL 문자를 사용하지 않는다. 대신 줄의 끝을 표현하기 위해 ‘\n’을 사용한다.
- 텍스트 파일
- 소스파일이나 메모장 파일
- 아스키 코드를 이용한 문자들이 포함
- 모니터, 키보드, 프린터 등이 문자 데이터만을 처리
- 연속적인 줄들로 구성
- 각 줄은 줄의 끝을 알리는 문자로 종료
- 윈도우즈는 캐리지 리턴과 줄바꿈 문자의 조합(CR-LF)으로 줄의 끝
- 유닉스는 줄바꿈 문자(LF)로 줄의 끝
- 매킨토시는 캐리지 리턴(CR)로 줄의 끝
- 이진 파일
- 사람은 못 읽고 컴퓨터는 읽는 파일
- 텍스트 파일과 달리 줄들로 분리되어 있지 않음 -> 줄의 끝 필요(x)
- 이진수 형태로 저장
- NULL, CR, LF 모두 단순한 데이터 취급
- 실행 파일, 사운드 파일, 이미지 파일
-
파일 열기 -> 읽기와 쓰기 -> 파일 닫기
- 파일 열기
- File *fopen(const char *name(파일 이름), const char *mode(파일 모드))
- 만약 실패하면 NULL포인터 반환
-
파일 모드
“r" : 읽기 모드로 파일을 연다. 파일이 없으면 오류가 발생한다. "w" : 쓰기 모드로 파일을 생성한다. 파일이 이미 존재하면 기존 내용은 지워진다. “a" : 추가 모드로 파일을 연다. 파일이 이미 존재하면 데이터가 파일의 끝에 추가된다. 파일이 없으면 새로운 파일을 만든다. “r+" : 읽기 모드로 파일을 연다. 쓰기 모드로 전환할 수 있다. 파일이 반드시 존재해야 한다. “w+" : 쓰기 모드로 새로운 파일을 생성한다. 읽기 모드로 전환할 수 있다. 파일이 이미 존재하면 기존 내용은 지워진다. “a+" : 추가 모드로 파일을 연다. 읽기 모드로 전환할 수 있다. 데이터를 추가하면 EOF 마커를 추가된 데이터의 뒤로 이동한다. 파일이 없으면 새로운 파일을 만든다. “t" : 텍스트 파일 모드로 파일을 연다. "b" : 이진 파일 모드로 파일을 연다. "a", "a+" : 추가 모드 "r+", "w+", "a+" : 수정 모드(전환하려면 fflush(), fsetpos(), fseek(), rewind() 중의 하나를 호출해야 한다)
- 파일 닫기
- fclose() : 성공하면 0, 실패하면 -1 반환
- 파일 삭제
- remove() : 성공하면 0, 실패하면 -1 반환
- 기타 함수들
- feof() : 파일의 끝에 도달되면 true를 반환한다
- rename(const char *oldname, const char *newname) : 파일 이름 변경
- FILE *tmpfile() : 임시 파일 생성하여 반환
- ferror() : 스트림의 오류 상태를 반환한다. 오류가 발생하면 true를 반환
-
파일은 일련의 연속된 바이트라고 생각할 수 있다.
-
파일에는 사람이 읽을 수 있는 텍스트가 들어 있는 텍스트 파일과 사람이 읽을 수 없으나 컴퓨터는 읽을 수 있는 이진 파일이 있다.
-
파일을 여는 라이브러리 함수는 fopen() 이다.
-
fopen()은 FILE을 가리키는 포인터를 반환한다.
-
텍스트 파일 읽기와 쓰기 1) 문자 단위 입출력
입력(파일에서 문자를 가져옴) : fgetc(FILE *fp) 출력(파일에 문자를 작성) : fputc(int c, FILE *fp)
2) 문자열 단위 입출력
입력 : fgets(char *s(문자열 저장), int n(최대 개수), FILE *fp) 출력 : fputs(char *s, FILE *fp)
3) 형식화된 입출력
입력 : fprintf(FILE *fp, const char *format) 출력 : fscanf(FILE *fp, const char *format)
-
fgetc()의 반환형은 int이다.
-
텍스트 파일에서 하나의 줄을 읽어서 반환하는 함수는 fgets()이다.
-
텍스트 파일에 실수나 정수를 문자열로 변경하여 저장할 때 사용하는 함수는 fprintf()이다.
-
텍스트 파일에서 실수나 정수를 읽는 함수는 fscanf()이다.
#include <stdio.h> #include <stdlib.h> int main(void) { FILE *fp; char fname[100]; int number, count = 0; char name[20]; float score, total = 0.0; printf("성적 파일의 이름을 입력하시오: "); scanf("%s", fname); // 성적 파일을 쓰기 모드로 연다 if((fp = fopen(fname, "w")) == NULL) { fprintf(stderr, "성적 파일 %s을 열 수 없습니다.\n", fname); exit(1); } // 사용자로부터 학번, 이름, 성적을 입력받아서 파일에 저장한다. while(1) { printf("학번, 이름, 성적을 입력하세요:(음수이면 종료)"); scanf("%d", &number); if(number < 0) break; scanf("%s %f", name, &score); // 데이터는 모두 문자열로 변환 fprintf(fp, "%d %s %f\n", number, name, score); } fclose(fp); // 성적 파일을 읽기 모드로 연다. if((fp = fopen(fname, "r")) == NULL) { fprintf(stderr, "성적 파일 %s을 열 수 없습니다.\n", fname); exit(1); } // 파일에서 성적을 읽어서 평균을 구한다. while(!feof(fp)) { fscanf(fp, "%d %s %f", &number, name, &score); total += score; count++; } printf("평균 = %f\n", total / count); fclose(fp); return 0; }
- 이진 파일 읽기와 쓰기
-
텍스트 파일과의 차이점
1) 텍스트 파일에서는 모든 정보가 문자열로 변환되어 저장
ex)123456 -> '1' '2' '3' '4' '5' '6'으로 변환되어 출력(fprintf) fscanf() -> 문자를 읽어서 숫자로 변환
2) 이진 파일은 데이터가 직접 저장(이진수 형태로)
3) 이진 파일의 장점
효율성 -> 변환 과정 없으므로 시간과 공간 절약 대량의 데이터를 한번에 기록할 때 편리함
4) 이진 파일의 단점
인간이 파일의 내용을 확인하기 어려움 이식성이 떨어짐
- 이진 파일 쓰기
- 파일 모드에서 “b”를 붙여줌
- 이진 파일 출력은 메모리 블록에 있는 데이터를 디스크 파일로 직접 저장
- fwrite(buffer(메모리 블록 주소), sizeof(int)(항목의 크기), SIZE(항목의 개수), fp)
- 항목의 크기는 바이트 단위
- 이진 파일 읽기
- fread(buffer(메모리 블록 주소), sizeof(int)(항목의 크기), SIZE(항목의 개수), fp)
-
- 버퍼링
- 버퍼는 파일로부터 읽고 쓰는 데이터의 임시 저장 장소로 이용되는 메모리의 블록
- 버퍼를 두는 이유는 디스크에서 물리적으로 데이터를 읽을 때는 상당한 시간이 걸리기 때문에 한 번 읽을 때 많이 읽어두고 나중에 다음 데이터가 필요하면 바로 버퍼에서 갖다 주는 것이다.
- fflush(fp)
- 버퍼가 필요 없는 경우 -> setbuf(fp, NULL)
- 데이터베이스
- 데이터를 모아 놓은 것으로 손쉽게 검색 가능한 저장 기법
- 저장 단위는 레코드
-
문자 데이터가 아니고 이진 데이터가 저장되어 있는 파일을 이진 파일
-
이진 파일을 생성할 때 사용하는 함수는 fopen()이다.
-
읽기 전용 이진 파일을 생성할 때 사용하는 파일 모드는 “rb”이다.
-
임의 접근
1) 순차 접근모든 데이터를 파일의 처음부터 순차적으로 읽거나 기록 앞부분을 읽지 않고 중간이나 마지막으로 못감
2) 임의 접근
파일의 어느 위치에서든지 읽기와 쓰기 가능
- 임의 접근의 원리
- 파일 포인터
- 읽기와 쓰기 동작이 현재 어떤 위치에서 이루어지는 지 나타내는 것
- 새 파일이면 파일 포인터는 0의 값(파일의 시작 부분)
- 기존 파일에서 추가 모드에서는 파일의 끝
- 기존 파일에서 다른 모드는 파일의 시작 부분
- 위치 표시자를 조작하는 함수는 fseek()
1) fseek(FILE *fp, long offset(거리), int origin(기준 위치))
offset은 기준 위치로부터 위치 표시자가 이동하는 거리 offset이 양수이면 앞으로, 음수이면 뒤로 간다 origin SEEK_SET(0, 파일의 시작) SEEK_CUR(1, 현재 위치) SEEK_END(2, 파일의 끝) - 성공하면 0을, 실패하면 0이 아닌 값을 반환 fseek(fp, 50L, SEEK_CUR) // 현재 위치에서 50바이트 이동 fseek(fp, -20L, SEEK_END) // 파일의 끝에서 20바이트 앞으로 이동
2) rewind(FILE *fp)
위치 표시자가 0으로 설정 파일을 읽은 다음, 다시 읽고자 할 때 사용
3) ftell(FILE *fp)
현재의 위치 표시자 값을 long형으로 반환
4) feof(FILE *fp)
파일의 끝
- 파일 포인터
-
파일의 처음부터 순차적으로 읽거나 쓰는 방법을 순차접근이라고 한다.
-
파일의 어느 위치에서나 읽고 쓰기가 가능한 방법을 임의접근이라 한다.
-
파일에서 읽기나 쓰기가 수행되면 파일의 현재 위치를 표시하는 파일 위치 표시자가 갱신된다.
-
파일의 위치 표시자를 알아내는 함수는 ftell()이다.
#include <stdio.h> #include <stdlib.h> #define SIZE 1000 void init_table(int table[], int size); int main(void) { int table[SIZE]; int n, data; long pos; FILE *fp = NULL; init_table(table, SIZE); // 이진 파일을 쓰기 모드로 연다. if((fp = fopen("sample.dat", "wb")) == NULL) { fprintf(stderr, "출력을 위한 파일을 열 수 없습니다.\n"); exit(1); } // table 배열에 SIZE 개수만큼 4바이트씩 저장 fwrite(table, sizeof(int), SIZE, fp); fclose(fp); // 이진 파일을 읽기 모드로 연다. if((fp = fopen("sample.dat", "rb")) == NULL) { fprintf(stderr, "입력을 위한 파일을 열 수 없습니다.\n"); exit(1); } //사용자가 선택한 위치의 정수를 파일로부터 읽는다. while(1) { printf("파일에서의 위치를 입력하시오(0에서 %d, 종료 -1): ", SIZE - 1); scanf("%d", &n); if(n == -1) break; pos = (long)n * sizeof(int); fseek(fp, pos, SEEK_SET); fread(&data, sizeof(int), 1, fp); printf("%d 위치의 값은 %d입니다.\n", n, data); } fclose(fp); return 0; } // 배열을 인덱스의 제곱으로 채운다. void init_table(int table[], int size) { int i; for(i = 0; i < size; i++) table[i] = i * i; }
-
파일은 연속된 바이트의 모임이라고 생각할 수 있다.
-
스트림은 파일 구조체를 통하여 접근된다.
-
프로그램이 실행될 때 자동으로 만들어지는 3개의 스트림의 이름은 stdin, stdout, stderr이다.
-
파일에는 사람이 읽을 수 있는 텍스트가 들어 있는 텍스트파일과 사람은 읽을 수 없으나 컴퓨터는 읽을 수 있는 이진 파일이 있다.
-
\n 문자는 텍스트 파일의 끝을 나타내는 특수 문자이다.
-
읽고 쓸 수 있는 이진 파일을 생성할 때 사용하는 파일 모드는 “r+b”이다.
-
fgetc()의 반환형은 int형이다.
-
파일을 삭제하는 라이브러리 함수는 remove()이다.
-
파일의 어느 위치에서나 읽고 쓰기가 가능한 방법을 임의접근이라 한다.
- 파일의 위치 표시자를 알아내는 함수는 ftell()이다.