기초C언어 제14주 전처리 및 비트 필드, 스트림과 파일 입출력(15,16장) 컴퓨터시뮬레이션학과 2016년 봄학기

Slides:



Advertisements
Similar presentations
Chapter 12. 배열. 배열  동일한 항목들이 동일한 크기로 연속적으로 메모리에 저장되는 구조  동일한 자료 유형이 여러 개 필요한 경우에 이용할 수 있는 자료 구조.
Advertisements

6 장. printf 와 scanf 함수에 대한 고찰 printf 함수 이야기 printf 는 문자열을 출력하는 함수이다. – 예제 printf1.c 참조 printf 는 특수 문자 출력이 가능하다. 특수 문자의 미 \a 경고음 소리 발생 \b 백스페이스 (backspace)
1 08 배열. 한국대학교 객체지향연구소 2 C 로 배우는 프로그래밍 기초 2 nd Edition 배열  동일한 자료유형의 여러 변수를 일괄 선언  연속적인 항목들이 동일한 크기로 메모리에 저장되는 구조  동일한 자료 유형이 여러 개 필요한 경우에 이용할 수 있는.
Format String Attack! 포맷 스트링 공격 경일대학교 사이버보안학과 학년 남주호.
제12장 표준 입출력과 파일 입출력.
ㅎㅎ 구조체 구조체 사용하기 함수 매개변수로서의 구조체 구조체 포인터와 레퍼런스 구조체 배열.
쉽게 풀어쓴 C언어 Express 제11장 포인터 C Express Slide 1 (of 27)
2장. 프로그램의 기본 구성. 2장. 프로그램의 기본 구성 2-1"Hello, World!" 들여다 보기 /* Hello.c */ #include int main(void) { printf("Hello, World! \n"); return 0;
컴퓨터프로그래밍 1주차실습자료 Visual Studio 2005 사용법 익히기.
제14장 동적 메모리.
쉽게 풀어쓴 C언어 Express 제16장 파일 입출력 C Express.
쉽게 풀어쓴 C언어 Express 제16장 파일 입출력 C Express.
쉽게 풀어쓴 C언어 Express 제16장 파일 입출력 C Express.
9장. C 언어의 핵심! 함수. 9장. C 언어의 핵심! 함수 9-1 함수의 정의와 선언 main 함수 다시 보기 : 함수의 기본 형태 { } 그림 9-1.
#include <stdio.h> int main(void) { float radius; // 원의 반지름
제 9 장 구조체와 공용체.
쉽게 풀어쓴 C언어 Express 제18장 입출력과 라이브러리 함수 C Express.
데이터 파일 C 데이터 파일과 스트림(Stream) 텍스트 파일 처리
윤성우의 열혈 C 프로그래밍 윤성우 저 열혈강의 C 프로그래밍 개정판 Chapter 12. 포인터의 이해.
쉽게 풀어쓴 C언어 Express 제16장 파일 입출력 C Express Slide 1 (of 23)
HW#1 Source 파일 제출 3.20(수)까지 제출 학번_이름_01.c
Part 14 파일 입출력 ©우균, 창병모 ©우균, 창병모.
제15장 파일 입출력 문자열을 출력하는 여러가지 방법 (15-2쪽) 문자열만 처리하는 입출력 함수
쉽게 풀어쓴 C언어 Express 제16장 스트림과 파일 입출력 C Express.
쉽게 풀어쓴 C언어 Express 제17장 동적메모리와 연결리스트 C Express Slide 1 (of 13)
제15장 전처리 및 비트연산.
쉽게 풀어쓴 C언어 Express 제15장 전처리 및 비트 필드 C Express.
11장. 포인터 01_ 포인터의 기본 02_ 포인터와 Const.
8장 함수 함수의 필요성 라이브러리 함수와 사용자 정의 함수 함수의 정의, 원형, 호출 배열을 함수 인자로 전달 재귀호출.
SqlParameter 클래스 선문 비트 18기 발표자 : 박성한.
컴퓨터 프로그래밍 기초 #02 : printf(), scanf()
12장 파일처리와 매크로 파일 입출력 함수 문자 입출력 함수 라인 입출력 함수 불록 입출력 함수 매크로.
11 표준 입출력과 파일 입출력 데이터를 읽고 쓰는 다양한 방법 익히기!.
6장. printf와 scanf 함수에 대한 고찰
쉽게 풀어쓴 C언어 Express 제15장 전처리 및 비트연산 C Express.
프로그래밍 랩 – 7주 리스트.
14장. 포인터와 함수에 대한 이해.
11장. 1차원 배열.
10장 C 표준 파일 입출력 子曰 學而時習(실습?)之 不亦悅乎.
JA A V W. 03.
사용자 함수 사용하기 함수 함수 정의 프로그램에서 특정한 기능을 수행하도록 만든 하나의 단위 작업
어서와 C언어는 처음이지 제14장.
제13장 전처리 및 기타기능.
3장 상수 변수 기본 자료형 키워드와 식별자 상수와 변수 기본 자료형 형변환 자료형의 재정의.
24장. 파일 입출력.
쉽게 풀어쓴 C언어 Express 제14장 포인터 활용 C Express Slide 1 (of 22)
Chapter6 : JVM과 메모리 6.1 JVM의 구조와 메모리 모델 6.2 프로그램 실행과 메모리 6.3 객체생성과 메모리
연산자 (Operator).
쉽게 풀어쓴 C언어 Express 제15장 전처리 및 비트연산 C Express Slide 1 (of 29)
컴퓨터 프로그래밍 기초 - 10th : 포인터 및 구조체 -
컴퓨터 프로그래밍 기초 - 8th : 함수와 변수 / 배열 -
문자열 컴퓨터시뮬레이션학과 2015년 봄학기 담당교수 : 이형원 E304호,
Choi Seong Yun 컴퓨터 프로그래밍 기초 #03 : 변수와 자료형 Choi Seong Yun
컴퓨터 프로그래밍 기초 [01] Visual Studio 설치 및 사용방법
4장. 데이터 표현 방식의 이해. 4장. 데이터 표현 방식의 이해 4-1 컴퓨터의 데이터 표현 진법에 대한 이해 n 진수 표현 방식 : n개의 문자를 이용해서 데이터를 표현 그림 4-1.
제 8장. 클래스의 활용 학기 프로그래밍언어및실습 (C++).
컴퓨터 프로그래밍 기초 - 9th : 배열 / 포인터 -
구조체(struct)와 공용체(union)
Chapter 12 파일 입출력.
실습과제 (변수와 자료형, ) 1. 다음 작업 (가), (나), (다)를 수행하는 프로그램 작성
동적메모리와 연결 리스트 컴퓨터시뮬레이션학과 2016년 봄학기 담당교수 : 이형원 E304호,
제 4 장 Record.
제 29 강 스트링(string) 다루기 s a i s . s T i h t g r i n.
C 13장. 입출력 라이브러리 #include <stdio.h> int main(void) { int num;
어서와 C언어는 처음이지 제21장.
개정판 누구나 즐기는 C언어 콘서트 제13장 동적 메모리 출처: pixabay.
개정판 누구나 즐기는 C언어 콘서트 제14장 전처리와 분할 컴파일리 출처: pixabay.
개정판 누구나 즐기는 C언어 콘서트 제12장 파일 입출력 출처: pixabay.
6 객체.
2019 2학기 9장 배열과 포인터 1. 주소, 주소연산자(&) 2. 포인터, 역참조연산자(*) 3. 배열과 포인터.
Presentation transcript:

기초C언어 제14주 전처리 및 비트 필드, 스트림과 파일 입출력(15,16장) 컴퓨터시뮬레이션학과 2016년 봄학기 담당교수 : 이형원 E304호, hwlee@inje.ac.kr

다음주 준비 17장 읽어 올 것 숙제 제출할 것

쉽게 풀어쓴 C언어 Express 제15장 전처리 및 비트연산 C Express

이번 장에서 학습할 내용 전처리와 기타 중요한 테마에 대하여 학습한다. 전처리 지시어 분할 컴파일 명령어 라인의 매개변수 디버깅 방법

전처리기란? 전처리기 (preprocessor)는 컴파일하기에 앞서서 소스 파일을 처리하는 컴파일러 의 한 부분 수고했어, 나머지는 나한테 맡겨! #include, #define만 처리 합니다. 소스 파일 임시 파일 오브젝트 파일 전처리기 컴파일러

전처리기의 요약 지시어 의미 #define 매크로 정의 #include 파일 포함 #undef 매크로 정의 해제 #if 조건이 참일 경우 #else 조건이 거짓일 경우 #endif 조건 처리 문장 종료 #ifdef 매크로가 정의되어 있는 경우 #ifndef 매크로가 정의되어 있지 않은 경우 #line 행번호 출력 #pragma 시스템에 따라 의미가 다름

단순 매크로 단순 매크로(macro): 숫자 상수를 기호 상수로 만든 것 (예) #define MAX_SIZE 100 #define PI 3.141592 #define EPS 1.0e-9

단순 매크로 #define MAX_SIZE 100 . . . while(i<MAX_SIZE) { sum += i; } . . . while(i<100) { sum += i; i++; } 전처리기

단순 매크로의 장점 프로그램의 가독성을 높인다. 상수의 변경이 용이하다. 기호 상수를 사용하는 경우 숫자를 사용하는 경우

단순 매크로의 예 #define PI 3.141592 // 원주율 #define TWOPI (3.141592 * 2.0) // 원주율의 2배 #define MAX_INT 2147483647 // 최대정수 #define EOF (-1) // 파일의 끝표시 #define MAX_STUDENTS 2000 // 최대 학생수 #define EPS 1.0e-9 // 실수의 계산 한계 #define DIGITS "0123456789" // 문자 상수 정의 #define BRACKET "(){}[]" // 문자 상수 정의 #define getchar() getc(stdin) // stdio.h에 정의 #define putchar() putc(stdout) // stdio.h에 정의 2147483647보다는 MAX_INT가 낫죠 사람은 숫자보다 기호를 잘 기억합니다.

예제 #include <stdio.h> #define AND && #define OR || #define NOT ! #define IS == #define ISNOT != int search(int list[], int n, int key) { int i = 0; while( i < n AND list[i] != key ) i++; if( i IS n ) return -1; else return i; } C프로그램을 다른 언어처럼 작성할 수 있습니다. && ==

예제 int main(void) { int m[] = { 1, 2, 3, 4, 5, 6, 7 }; printf("%d\n", search(m, sizeof(m)/sizeof(m[0]), 5)); return 0; } 4 계속하려면 아무 키나 누르십시오 . . .

중간 점검 1. #define을 이용하여서 1234를 KEY로 정의하여 보라. 2. #define을 이용하여서 scanf를 INPUT으로 정의하여 보라.

#define SQUARE(x) ((x) * (x)) 함수 매크로 함수 매크로(function-like macro)란 매크로가 함수처럼 매개 변수를 가지 는 것 (예) #define SQUARE(x) ((x) * (x)) #define SQUARE(x) ((x) * (x)) 전처리기 v = SQUARE(3); v = ((3)*(3));

함수 매크로의 예 #define SUM(x, y) ((x) + (y)) #define AVERAGE(x, y, z) (( (x) + (y) + (z) ) / 3 ) #define MAX(x,y) ( (x) > (y) ) ? (x) : (y) #define MIN(x,y) ( (x) < (y) ) ? (x) : (y)

주의할 점 #define SQUARE(x) x*x // 위험 !! v = SQUARE(a+b); v = a + b*a + b; 함수 매크로에서는 매개 변수를 괄호로 둘러싸는 것이 좋습니다. #define SQUARE(x) (x)*(x) // 올바른 형태

함수 매크로의 장단점 mode==1 ) \ 함수 매크로의 장단점 간단한 기능은 매크로를 사용 매크로를 한줄 이상 연장하는 방법 함수 호출 단계가 필요없어 실행 속도가 빠르다. 소스 코드의 길이가 길어진다. 간단한 기능은 매크로를 사용 #define MIN(x, y) ((x) < (y) ? (x) : (y)) #define ABS(x) ((x) > 0 ? (x) : -(x)) 매크로를 한줄 이상 연장하는 방법 #define PRINT(x) if( debug==1 && \ mode==1 ) \ printf(“%d”, x);

예제 #1 // 매크로 예제 #include <stdio.h> #define SQUARE(x) ((x) * (x)) int main(void) { int x = 2; printf("%d\n", SQUARE(x)); printf("%d\n", SQUARE(3)); printf("%f\n", SQUARE(1.2)); // 실수에도 적용 가능 printf("%d\n", SQUARE(x+3)); printf("%d\n", 100/SQUARE(x)); printf("%d\n", SQUARE(++x)); // 논리 오류 return 0; } ((++x) * (++x)) 4 9 1.440000 25 16

# 연산자 x=5 exp=5 PRINT(x)와 같이 호출하면 와 같이 출력하는 매크로 작성 다음과 같이 작성하면 잘못된 결과가 나온다. #define PRINT(exp) printf("exp=%d\n", exp); exp=5

#은 문자열 변환 연산자(Stringizing Operator)라고 불린다 #define PRINT(exp) printf(#exp" = %d\n",exp); PRINT(x); x=5

## 연산자 ## 연산자는 토큰 병합 연산자 (token-pasting operator) #define MAKE_NAME(n) v ## n MAKE_NAME(1)과 같이 호출된다고 가정하자. 매개 변수 n은 1로 치환되고 ## 연산자에 의하여 v와 1이 합쳐져서 하나의 토큰 v1 이 된다.

예제 #include <stdio.h> #define MAKE_NAME(n) v ## n #define PRINT(n) printf("v" #n " = %d\n", v ## n); int main(void) { int MAKE_NAME(1) = 10; // int MAKE_NAME(2) = 20; PRINT(1); // printf("v1 = %d\n", v1);과 같다. PRINT(2); // printf("v2 = %d\n", v2);과 같다. return 0; } v1 = 10 v2 = 20

내장 매크로 내장 매크로: 미리 정의된 매크로 printf("컴파일 날짜=%s\n", __DATE__); 설명 __DATE__ 이 매크로를 만나면 현재의 날짜(월 일 년)로 치환된다. __TIME__ 이 매크로를 만나면 현재의 시간(시:분:초)으로 치환된다. __LINE__ 이 매크로를 만나면 소스 파일에서의 현재의 라인 번호로 치환된다. __FILE__ 이 매크로를 만나면 소스 파일 이름으로 치환된다. printf("컴파일 날짜=%s\n", __DATE__); printf("치명적 에러 발생 파일 이름=%s 라인 번호= %d\n", __FILE__, __LINE__);

예제: ASSERT 매크로 #include <stdio.h> #define ASSERT(exp) { if (!(exp)) \ { printf("가정(" #exp ")이 소스 파일 %s %d번째 줄에서 실패.\n"\ ,__FILE__, __LINE__), exit(1);}} int main(void) { int sum; // 지역 변수의 초기값은 0이 아님 ASSERT(sum == 0); // sum의 값은 0이 되어야 함. return 0; } 매크로를 다음 줄로 연장할 때 사용 가정(sum == 0)이 소스 파일 c:\source\chapter15\macro3\macro3\macro3.c 11번째 줄에서 실패.

비트 관련 매크로 매크로들은 변수를 받아서 특정 비트값을 반환하거나 설정한다. GET_BIT()는 변수 w에서 k번째 비트의 값을 0 또는 1로 반환한다. #define GET_BIT(w, k) (((w) >> (k)) & 0x01) SET_BIT_ON()는 변수 w의 k번째 비트를 1로 설정하는 매크로이다. #define SET_BIT_ON(w, k) ((w) |= (0x01 << (k))) SET_BIT_OFF()는 변수 w의 k번째 비트를 0로 설정하는 매크로이다. #define SET_BIT_OFF(w, k) ((w) &= ~(0x01 << (k)))

예제: ASSERT 매크로 #include <stdio.h> #define GET_BIT(w, k) (((w) >> (k)) & 0x01) #define SET_BIT_ON(w, k) ((w) |= (0x01 << (k))) #define SET_BIT_OFF(w, k) ((w) &= ~(0x01 << (k))) int main(void) { int data=0; SET_BIT_ON(data, 2); printf("%08X\n", data); printf("%d\n", GET_BIT(data, 2)); SET_BIT_OFF(data, 2); return 0; } 00000004 1 00000000

중간 점검 함수 매크로와 함수 중에서 속도 면에서 유리한 것은? 주어진 수의 3제곱을 수행하는 함수 매크로를 정의하여 보자.

#ifdef #ifdef 매크로 문장1 // 매크로가 정의되었을 경우 … #else 문장2 // 매크로가 정의되지 않았을 경우 어떤 조건이 만족되었을 경우에만 컴파일하는 조건부 컴파일 지시 #ifdef 매크로 문장1 // 매크로가 정의되었을 경우 … #else 문장2 // 매크로가 정의되지 않았을 경우 #endif

#ifdef의 예

예제 #include <stdio.h> #define DELUXE int main(void) { #ifdef DELUXE printf("딜럭스 버전입니다. \n"); #endif return 0; } 딜럭스 버전입니다.

예제 LINUX 버전 WINDOWS 버전 #include <stdio.h> #define LINUX int main(void) { #ifdef LINUX ... #else #endif return 0; } LINUX 버전 WINDOWS 버전

Visual C++에서 설정하는 방법

#ifndef, #undef #ifndef #undef 어떤 매크로가 정의되어 있지 않으면 컴파일에 포함된다. 매크로의 정의를 취소한다

중간 점검 1. 전처리기 지시자 #ifdef을 사용하여 TEST가 정의되어 있는 경우에만 화면에 “TEST" 라고 출력하는 문장을 작성하여 보자.

#if 기호가 참으로 계산되면 컴파일 조건은 상수이어야 하고 논리, 관계 연산자 사용 가능

#if-#else-#endif (예) #if NATION == 1 #include "korea.h" #elif NATION == 2 #include "china.h" #else #include "usa.h" #endif

다양한 예 #if (VERSION > 3) // 가능! 버전이 3 이상이면 컴파일 ... #endif #if (AUTHOR == KIM) // 가능!! KIM은 다른 매크로 #if (VERSION*10 > 500 && LEVEL == BASIC) // 가능!! #if (VERSION > 3.0) // 오류 !! 버전 번호는 300과 같은 정수로 표시 #if (AUTHOR == "CHULSOO") // 오류 !! #if (VERSION > 300 || defined(DELUXE) )

조건부 컴파일을 이용하는 디버깅 #define DEBUG 1 ... #if DEBUG == 1 printf("현재 counter의 값은 %d입니다.\n", counter); #endif

조건부 컴파일을 이용하는 디버깅 #define DEBUG ... #ifdef DEBUG printf("현재 counter의 값은 %d입니다.\n", counter); #endif #if defined(DEBUG)

다수의 라인을 주석처리 #if 0 // 여기서부터 시작하여 ... #endif // 여기까지 주석 처리된다.

예제 정렬 알고리즘을 선택 #define SORT_METHOD 3 #if (SORT_METHOD == 1) ... // 선택정렬구현 #elif (SORT_METHOD == 2) ... // 버블정렬구현 #else ... // 퀵정렬구현 #endif 정렬 알고리즘을 선택

중간 점검 1. #if를 사용하여 DEBUG가 2일 경우에만 “DEBUG"가 나오도록 문장을 작성하라. 2. #if를 사용하여 DEBUG가 2이고 LEVEL이 3인 경우에만 “DEBUG"가 나오도록 문장을 작성하라.

헤더 파일 이중 포함 방지 /*** *stdio.h - definitions/declarations for standard I/O routines ****/ #ifndef _INC_STDIO #define _INC_STDIO .... #endif 헤더 파일이 포함되면 매크로가 정의되어서 이중 포함을 방지합니다.

다중 소스 파일 단일 소스 파일 다중 소스 파일 파일의 크기가 너무 커진다. 소스 파일을 다시 사용하기가 어려움 서로 관련된 코드만을 모아서 하나의 소스 파일로 할 수 있음 소스 파일을 재사용하기가 간편함

다중 소스 파일

예제 power.h // power.c에 대한 헤더 파일 #ifndef POWER_H multiple_source.c #define POWER_H double power(int x, int y); #endif multiple_source.c // 다중 소스 파일 #include <stdio.h> #include "power.h" int main(void) { int x,y; printf("x의 값을 입력하시오:"); scanf("%d", &x); printf("y의 값을 입력하시오:"); scanf("%d", &y); printf("%d의 %d 제곱값은 %f\n", x, y, power(x, y)); return 0; } power.c // 다중 소스 파일 #include "power.h“ double power(int x, int y) { double result = 1.0; int i; for(i = 0;i < y; i++) result *= x; return result; }

헤더 파일을 사용하지 않으면

헤더 파일을 사용하면

다중 소스 파일에서 외부 변수

비주얼 C++에서 다중 소스 파일

비주얼 C++에서 다중 소스 파일

비주얼 C++에서 다중 소스 파일

헤더 파일 이중 포함 방지 #include <stdio.h> #include "rect.h" #define DEBUG void draw_rect(const RECT *r) { #ifdef DEBUG printf("draw_area(x=%d, y=%d, w=%d, h=%d) \n", r->x, r->y, r->w, r->h); #endif } 구조체의 정의가 이중으로 포함되어서 오류가 발생한다.

헤더 파일 이중 포함 방지 #ifndef RECT_H #define RECT_H struct rect { int x, y, w, h; }; typedef struct rect RECT; void draw_rect(const RECT *); double calc_area(const RECT *); void move_rect(RECT *, int, int); #endif RECT_H가 정의되어 있지 않은 경우에만 포함시킨다. RECT_H 매크로를 정의한다.

중간 점검 다음 문장의 참 거짓을 말하라. “여러 소스 파일을 이용하는 것보다 하나의 소스 파 일로 만드는 편이 여러모로 유리하다.” 팩토리얼을 구하는 함수가 포함된 소스 파일과 관련 헤더 파일을 제작하여 보자.

비트 필드 구조체 멤버가 비트 단위로 나누어져 있는 구조체 struct 태그이름 { 자료형 멤버이름1: 비트수; 자료형 멤버이름2: 비트수; ... }; struct product { unsigned style : 3; unsigned size : 2; unsigned color : 1; };

bit_field.c // 비트 필드 구조체 #include <stdio.h> struct product { unsigned style : 3; unsigned size : 2; unsigned color : 1; }; int main(void) { struct product p1; p1.style = 5; p1.size = 3; p1.color = 1; printf("style=%d size=%d color=%d\n", p1.style, p1.size, p1.color); printf("sizeof(p1)=%d\n", sizeof(p1)); printf("p1=%x\n", p1); return 0; } style=5 size=3 color=1 sizeof(p1)=4 p1=ccccccfd

비트 필드 사용시에 주의점 비트 필드의 응용 분야: 하드웨어 포트 제어 struct product { long code; // ① 일반 멤버도 가능하다. unsigned style : 3; unsigned : 5; // ② 자리만 차지한다. unsigned size : 2; unsigned color : 1; unsigned : 0; // ③ 현재 워드의 남아있는 비트를 버린다. unsigned state : 3; // 여기서부터는 다음 워드에서 할당된다. }; 비트 필드의 응용 분야: 하드웨어 포트 제어

비트 필드의 장점 메모리가 절약된다. ON 또는 OFF의 상태만 가지는 변수를 저장할 때 32비트의 int형 변수를 사용하는 것보다 는 1비트 크기의 비트 필드를 사용하는 편이 훨씬 메모리를 절약한다.

실습: 전처리기 사용하기 원의 면적을 구하는 프로그램을 미국 버전과 한국 버전으로 작성한다. 미국 버전에서는 모든 메시지가 영어로 출력되고 단위도 인치가 된다. 한국 버전에서는 모든 메시지가 한글로 출력되고 단위도 cm가 된다. SQUARE() 함수 매크로도 억지로 사용하여 보자.

실행 결과 exp=5 Please enter radius of a circle(inch) : 100 area(100.000000) is called area of the circle is 31415.920000 exp=5 원의 반지름을 입력하시오(cm): 100 area(100.000000)가 호출되었음 원의 면적은 31415.920000입니다.

실습 예제 #include <stdio.h> #define USA #define DEBUG #ifndef PI #define PI 3.141592 #endif #ifndef SQUARE #define SQUARE(r) (r)*(r) double area(double radius) { double result=0.0; #ifdef DEBUG #ifdef USA printf("area(%f) is called \n", radius); #else printf("area(%f)가호출되었음radius); result = PI*SQUARE(radius); return result; }

실습 예제 int main(void){ double radius; #ifdef USA printf("Please enter radius of a circle(inch) : "); #else printf("원의 반지름을 입력하시오"); #endif scanf("%lf", &radius); printf("area of the circle is %f \n", area(radius)); printf("원의 면적은 %f입니다\n", area(radius)); return 0; }

도전문제 버전을 나타내는 매크로를 정의하고 버전이 100 이하이면 원의 면적을 계산할 수 없다는 메시지를 출력하고 종료하게끔, 위의 프로그램을 수정하여 보자. __DATE__와 __LINE__을 출력하여 보자.

중간 점검 1. 구조체의 일종으로 멤버들의 크기가 비트 단위로 나누어져 있는 구조체는 _____이다. 2. 비트 필드 구조체를 정의하는 경우, 자료형은 ______이나 ______을 사용하여야 한다.

Q & A

쉽게 풀어쓴 C언어 Express 제16장 파일 입출력 C Express

입출력에 관련된 개념들과 함수들에 대하여 학습한다. 이번 장에서 학습할 내용 입출력에 관련된 개념들과 함수들에 대하여 학습한다. 스트립의 개념 표준 입출력 파일 입출력 입출력 관련 함수

스트림의 개념 스트림(stream): 입력과 출력을 바이트(byte)들의 흐름으로 생각하는 것

스트림과 버퍼 스트림에는 기본적으로 버퍼가 포함되어 있다.

표준 입출력 스트림 기본적인 스트림들은 프로그래머가 생성하지 않아도 자동으로 생성된다. 이름 스트림 연결 장치 stdin 표준 입력 스트림 키보드 stdout 표준 출력 스트림 모니터의 화면 stderr 표준 오류 스트림  

입출력 함수의 분류 사용하는 스트림에 따른 분류 표준 입출력 스트림을 사용하여 입출력을 하는 함수 스트림을 구체적으로 명시해 주어야 하는 입출력 함수 스트림 형식 표준 스트림 일반 스트림 설명 형식이 없는 입출력(문자 형태) getchar() fgetc(FILE *f,...) 문자 입력 함수 putchar() fputc(FILE *f,...) 문자 출력 함수 gets() fgets(FILE *f,...) 문자열 입력 함수 puts() fputs(FILE *f,...) 문자열 출력 함수 형식이 있는 입출력(정수, 실수,..) printf() fprintf(FILE *f,...) 형식화된 출력 함수 scanf() fscanf(FILE *f,...) 형식화된 입력 함수

입출력 함수의 분류 데이터의 형식에 따른 분류 getchar()나 putchar()처럼 문자 형태의 데이터를 받아들이는 입출력 printf()나 scanf()처럼 구체적인 형식을 지정할 수 있는 입출력 스트림 형식 표준 스트림 일반 스트림 설명 형식이 없는 입출력(문자 형태) getchar() fgetc(FILE *f,...) 문자 입력 함수 putchar() fputc(FILE *f,...) 문자 출력 함수 gets() fgets(FILE *f,...) 문자열 입력 함수 puts() fputs(FILE *f,...) 문자열 출력 함수 형식이 있는 입출력(정수, 실수,..) printf() fprintf(FILE *f,...) 형식화된 출력 함수 scanf() fscanf(FILE *f,...) 형식화된 입력 함수

스트림과 파일 스트림은 구체적으로 FILE 구조체를 통하여 구현 FILE은 stdio.h에 정의되어 있다.

중간 점검 1. C에서의 모든 입력과 출력을 ______형식으로 처리된다. 2. 스트림은 모든 입력과 출력을 _____들의 흐름으로 간주한다. 3. 스트림의 최대 장점은 __________이다. 4. 입력을 위한 표준적인 스트림은 _________이고 기본적으로 ______ 장치와 연결된다. 5. 출력을 위한 표준적인 스트림은 _________이고 기본적으로 ______ 장치와 연결된다.

printf()를 이용한 출력 형식 제어 문자열의 구조 % [플래그] [필드폭] [.정밀도] 형식 출력 형식 % [플래그] [필드폭] [.정밀도] 형식 정밀도는 소수점 이하 자릿수의 개수가 된다. 출력의 정렬과 부호 출력, 공백 문자 출력, 소수점, 8진수와 16진수 접두사 출력 데이터가 출력되는 필드의 크기

형식지정자

실수의 형식

필드폭과 정밀도

필드폭과 정밀도

플래그 기호 의미 기본값 - 출력 필드에서 출력값을 왼쪽 정렬한다. 오른쪽 정렬된다. + 결과 값을 출력할 때 항상 +와 -의 부호를 붙인다. 음수일 때만 - 부호를 붙인다. 출력값 앞에 공백 문자 대신에 0으로 채운다. -와 0이 동시에 있으면 0은 무시된다. 만약 정수 출력의 경우, 정밀도가 지정되면 역시 0은 무시된다(예를 들어서 %08.5). 채우지 않는다. blank(‘ ’) 출력값 앞에 양수나 영인 경우에는 부호대신 공백을 출력한다. 음수일 때는 -가 붙여진다. + 플래그가 있으면 무시된다. 공백을 출력하지 않는다. # 8진수 출력 시에는 출력값 앞에 0을 붙이고 16진수 출력 시에는 0x를 붙인다. 붙이지 않는다.

플래그

중간 점검 1. printf()에서 변수나 수식의 값을 출력하는 형식을 지정하는 문자열은 __________이다. 2. printf()에서 정렬(alignment)을 구체적으로 지시하지 않으면 기본 적으로 _______정렬된다. 3. 실수를 지수 표기법으로 출력하는데 사용되는 형식 지정자는 ______ 이다. 4. 정수를 필드폭 6으로 출력하려면 %___d로 하여야 한다. 5. 실수를 필드폭 10이고 소수점 이하 자리수를 6자리로 출력하려 면 %_____f로 하여야 한다. 6. 출력값을 왼쪽 정렬시키는 플래그는 ___이다. 7. 실수 출력의 경우, 정밀도를 지정하지 않았을 경우, 소수점 이하 자리 수는 기본적으로 ______개가 된다.

scanf()를 이용한 입력 문자열 형태의 입력을 사용자가 원하는 형식으로 변환한다.

필드폭 지정하여 읽기

필드로 지정하여서 읽기 #include <stdio.h> int main(void) { int a, b; printf("5개의 숫자로 이루어진 정수를 입력하시오: "); scanf("%3d%3d", &a, &b); printf("입력된 정수는 %d, %d\n", a, b); return 0; } 3글자씩 나우어서 읽는다. 5개의 숫자로 이루어진 정수를 입력하시오: 123456 입력된 정수는 123, 456

필드로 지정하여서 읽기 #include <stdio.h> int main(void) { int d, o, x; scanf("%d %o %x", &d, &o, &x); printf("d=%d o=%d x=%d\n", d, o, x); return 0; } 10 d=10 o=8 x=16

문자와 문자열 입력 분류 형식 지정자 설명 문자형 %c char형으로 입력받음 %s 공백 문자가 아닌 문자부터 공백 문자가 나올 때까지를 문자열로 변환하여 입력받음. %[abc] 대괄호 안에 있는 문자 a,b,c로만 이루어진 문자열을 읽어 들인다. %[^abc] 대괄호 안에 있는 문자 a,b,c만을 제외하고 다른 문자들로 이루어진 문자열을 읽어 들인다. %[0-9] 0에서 9까지의 범위에 있는 문자들로 이루어진 문자열을 읽어 들인다.

문자와 문자열 읽기

scanf6.c #include <stdio.h> int main(void) { char c; char 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; } 문자열을 입력하시오:abcdef 입력된 문자열=abc

반환값 이용 #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 a b c 입력이 올바르지 않음

scanf() 사용시 주의점 입력값을 저장할 변수의 주소를 전달 배열의 이름은 배열을 가리키는 포인터 int i; scanf("%d", i); // 오류!! 배열의 이름은 배열을 가리키는 포인터 int str[80]; scanf("%s", str); // 올바름 scanf("%s", &str); // 오류!!

scanf() 사용시 주의점 충분한 공간을 확보 int str[80]; scanf("%s", str); // 입력된 문자의 개수가 79를 초과하면 치명적인 오류 발생 scanf()의 형식 제어 문자열의 끝에 줄바꿈 문자 '\n'을 사용하는 것은 해당 문자가 반 드시 입력되어야 한다는 의미 scanf("%d\n", &i);// 잘못됨!!

중간 점검 1. scanf()에서 double 값을 입력받을 때 사용하는 형식 지정자는 ______이다.

파일이 필요한 이유

파일의 개념 C에서의 파일은 일련의 연속된 바이트 모든 파일 데이터들은 결국은 바이트로 바뀌어서 파일에 저장 이들 바이트들을 어떻게 해석하느냐는 전적으로 프로그래머의 책임

파일 파일에 4개의 바이트가 들어 있을 때 이것을 int형의 정수 데이터로도 해석할 수 있 고 아니면 float형 실수 데이터로도 해석할 수 있다 하나의 정수? 하나의 실수? 4개의 문자? 파일 0x36 0x34 0x31 0x0

텍스트 파일(text file) 텍스트 파일은 사람이 읽을 수 있는 텍스트가 들어 있는 파일 (예) C 프로그램 소스 파일이나 메모장 파일 텍스트 파일은 아스키 코드를 이용하여 저장 텍스트 파일은 연속적인 라인들로 구성 ‘W’ ‘O’ ‘R’ ‘L’ ‘D’ ‘\r’ ‘\n’ 윈도우, MS_DOS 유닉스 매킨토시 C언어

이진 파일(binary file) 이진 파일은 사람이 읽을 수는 없으나 컴퓨터는 읽을 수 있는 파일 이진 데이터가 직접 저장되어 있는 파일 이진 파일은 텍스트 파일과는 달리 라인들로 분리되지 않는다. 모든 데이터들은 문자열로 변환되지 않고 입출력 이진 파일은 특정 프로그램에 의해서만 판독이 가능 (예) C 프로그램 실행 파일, 사운드 파일, 이미지 파일

파일 처리의 개요 파일을 다룰 때는 반드시 다음과 같은 순서를 지켜야 한다. 디스크 파일은 FILE 구조체를 이용하여 접근 FILE 구조체를 가리키는 포인터를 파일 포인터(file pointer) 파일 열기 파일 읽기와 쓰기 파일 닫기

파일 열기 FILE *fopen(const char *name, const char *mode) FILE *fp; 파일에서 데이터를 읽거나 쓸 수 있도록 모든 준비를 마치는 것 첫 번째 매개 변수인 name은 파일의 이름 두 번째 매개 변수인 mode는 파일을 여는 모드를 의미 FILE *fopen(const char *name, const char *mode) FILE *fp; fp = open(“test.txt”, “w”);

파일 모드 모드 설명 “r” 읽기 모드로 파일을 연다. “w” 쓰기 모드로 파일을 생성한다. 만약 파일이 존재하지 않으면 파일이 생성된다. 파일이 이미 존재하면 기존의 내용이 지워진다. “a“ 추가 모드로 파일을 연다. 만약 똑같은 이름의 기존의 파일이 있으면 데이터가 파일의 끝에 추가된다. 파일이 없으면 새로운 파일을 만든다. “r+” 읽기 모드로 파일을 연다. 쓰기 모드로 전환할 수 있다. 파일이 반드시 존재하여야 한다. “w+” 쓰기 모드로 파일을 생성한다. 읽기 모드로 전환할 수 있다. 파일이 존재하면 기존의 데이터가 지워진다. “a+” 추가 모드로 파일을 연다. 읽기 모드로 전환할 수 있다. 데이터를 추가하면 EOF 마커를 추가된 데이터의 뒤로 이동한다. 파일이 없으면 새로운 파일을 만든다. “b” 이진 파일 모드로 파일을 연다.

주의할 점 기본적인 파일 모드에 "t"나 "b"를 붙일 수 있다. "a" 나 "a+" 모드는 추가 모드(append mode)라고 한다. 추가 모드로 파일이 열리 면, 모든 쓰기 동작은 파일의 끝에서 일어난다. 따라서 파일 안에 있었던 기존의 데 이터는 절대 지워지지 않는다. "r+", "w+", "a+" 파일 모드가 지정되면 읽고 쓰기가 모두 가능하다. 이러한 모드를 수 정 모드(update mode)라고 한다. 읽기 모드에서 쓰기 모드로, 또는 쓰기 모드에서 읽기 모드로 전환하려면 반드시 fflush(), fsetpos(), fseek(), rewind() 중의 하나를 호출하여야 한다.

파일 모드

file_open.c #include <stdio.h> int main(void) { FILE *fp = NULL; fp = fopen("sample.txt", "w"); if( fp == NULL ) printf("파일 열기 실패\n"); else printf("파일 열기 성공\n"); fclose(fp); return 0; } sample.txt 파일 열기 성공

파일 닫기와 삭제 파일을 닫는 함수 파일을 삭제하는 함수 int fclose( FILE *stream ); int remove(const char *path) #include <stdio.h> int main( void ) { if( remove( "sample.txt" ) == -1 ) printf( "sample.txt를 삭제할 수 없습니다.\n" ); else printf( "sample.txt를 삭제하였습니다.\n" ); return 0; }

중간 점검 1. 파일은 일련의 연속된 ______라고 생각할 수 있다. 2. 파일에는 사람이 읽을 수 있는 텍스트가 들어 있는 _____파일과 사람은 읽을 수 없으 나 컴퓨터는 읽을 수 있는 ______파일이 있다. 3. 파일을 여는 라이브러리 함수는 ______이다. 4. fopen()은 ________을 가리키는 포인터를 반환한다.

크게 나누면 텍스트 입출력 함수와 이진 데이터 입출력으로 나눌 수 있습니다. 파일 입출력 함수 파일 입출력 라이브러리 함수 종류 설명 입력 함수 출력 함수 문자 단위 문자 단위로 입출력 int fgetc(FILE *fp) int fputc(int c, FILE *fp) 문자열 단위 문자열 단위로 입출력 char *fgets(FILE *fp) int fputs(const char *s, FILE *fp) 서식화된 입출력 형식 지정 입출력 int fscanf(FILE *fp, ...) int fprintf(FILE *fp,...) 이진 데이터 이진 데이터 입출력 fread() fwrite() 크게 나누면 텍스트 입출력 함수와 이진 데이터 입출력으로 나눌 수 있습니다.

문자 단위 입출력 문자열 입출력 함수 문자 입출력 함수 F I L E FILE INPUT 파일 포인터 int fgetc( FILE *fp ); int fputc( int c, FILE *fp ); F I L E 문자열 입출력 함수 문자열의 크기 char *fgets( char *s, int n, FILE *fp ); int fputs( char *s, FILE *fp ); FILE INPUT

문자 단위 입출력 #include <stdio.h> int main(void) { FILE *fp = NULL;   #include <stdio.h> int main(void) { FILE *fp = NULL;   fp = fopen("sample.txt", "w"); if( fp == NULL ) printf("파일 열기 실패\n"); else printf("파일 열기 성공\n"); fputc('a', fp); fputc('b', fp); fputc('c', fp);   fclose(fp); return 0; } sample.txt abc 파일 열기 성공

문자 단위 입출력 #include <stdio.h> int main(void) { FILE *fp = NULL;   #include <stdio.h> int main(void) { FILE *fp = NULL; int c; fp = fopen("sample.txt", "r"); if( fp == NULL ) printf("파일 열기 실패\n"); else printf("파일 열기 성공\n"); while((c = fgetc(fp)) != EOF ) putchar(c); fclose(fp); return 0; } sample.txt abc 파일 열기 성공 abc

문자열 단위 입출력 #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")) == NULL ) fprintf(stderr,"원본 파일 %s을 열 수 없습니다.\n", file1); exit(1); }

문자열 단위 입출력 if( (fp2 = fopen(file2, "w")) == NULL ) { // 두번째 파일을 쓰기 모드로 연다. if( (fp2 = fopen(file2, "w")) == NULL ) { 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

예제 proverb.txt #include <stdio.h> #include <string.h> int main(void) { FILE *fp; char fname[128]; char buffer[256]; char word[256]; int line_num = 0; printf("입력 파일 이름을 입력하시오: "); scanf("%s", fname); printf("탐색할 단어를 입력하시오: "); scanf("%s", word); proverb.txt A chain is only as strong as its weakest link A change is as good as a rest A fool and his money are soon parted A friend in need is a friend indeed A good beginning makes a good ending A little knowledge is a dangerous thing …

예제 { fprintf(stderr,"파일 %s을 열 수 없습니다.\n", fname); exit(1); } // 파일을 읽기 모드로 연다. if( (fp = fopen(fname, "r")) == NULL ) { fprintf(stderr,"파일 %s을 열 수 없습니다.\n", fname); exit(1); } while( fgets(buffer, 256, fp) ) line_num++; if( strstr(buffer, word) ) printf("%s: %d 단어 %s이 발견되었습니다.\n", fname, line_num, word ); fclose(fp); return 0; 입력 파일 이름을 입력하시오: proverb.txt 탐색할 단어를 입력하시오: house proverb.txt: 7 단어 house이 발견되었습니다. proverb.txt: 8 단어 house이 발견되었습니다.

%d와 같은 특정한 형식을 지정하여 파일에 출력할 수 있습니다. 형식화된 출력 int fprintf( FILE *fp, const char *format, ...); sample.txt int main(void) { int i = 23; float f = 1.2345; FILE *fp; fp = fopen("sample.txt", "w"); if( fp != NULL ) fprintf(fp, "%10d %16.3f", i, f); fclose(fp); } 23 1.235 %d와 같은 특정한 형식을 지정하여 파일에 출력할 수 있습니다.

형식화된 입력 int fscanf( FILE *fp, const char *format, ...); int main(void) { int i; float f; FILE *fp; fp = fopen("sample.txt", "r"); if( fp != NULL ) fscanf(fp, "%d %f", &i, &f); printf(“%d %f”, i, f); fclose(fp): } 100 12.345 %d와 같은 특정한 형식을 지정하여 파일에 입력할 수 있습니다. sample.txt 100 12.345

예제 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; 성적 파일 이름을 입력하시오: score.txt 학번, 이름, 성적을 입력하시요: (음수이면 종료) 1 KIM 90.2 학번, 이름, 성적을 입력하시요: (음수이면 종료) 2 PARK 30.5 학번, 이름, 성적을 입력하시요: (음수이면 종료) 3 MIN 56.8 학번, 이름, 성적을 입력하시요: (음수이면 종료)-1 평균 = 58.575001

중간 점검 1. fgetc()의 반환형은 _____형이다. 2. 파일에서 하나의 라인을 읽어서 반환하는 함수는 ______이다. 3. 텍스트 파일에 실수나 정수를 문자열로 변경하여 저장할 때 사용하는 함수는 _____ 이다. 4. 텍스트 파일에서 실수나 정수를 읽는 함수는 _____이다.

이진 파일 쓰기와 읽기 텍스트 파일과 이진 파일의 차이점 텍스트 파일: 모든 데이터가 아스키 코드로 변환되어서 저장됨 이진 파일: 컴퓨터에서 데이터를 표현하는 방식 그대로 저장

이진 파일의 생성 파일 모드 설명 “rb" 읽기 모드 + 이진 파일 모드 “wb" 쓰기 모드 + 이진 파일 모드 “ab" 추가 모드 + 이진 파일 모드 “rb+" 읽고 쓰기 모드 + 이진 파일 모드 "wb+" 쓰고 읽기 모드 + 이진 파일 모드 int main(void) { FILE *fp = NULL; fp = fopen("binary.txt", "rb"); if( fp == NULL ) printf("이진 파일 열기에 실패하였습니다.\n"); else printf("이진 파일 열기에 성공하였습니다.\n"); if( fp != NULL ) fclose(fp); }

이진 파일 쓰기 size_t fwrite( void *buffer, size_t size, size_t count, FILE *fp);

이진 파일 쓰기 count buffer size #include <stdio.h> int main(void) { int buffer[] = { 10, 20, 30, 40, 50 }; FILE *fp = NULL; size_t i, size, count; fp = fopen("binary.bin", "wb"); if( fp == NULL ) fprintf(stderr, "binary.txt 파일을 열 수 없습니다."); exit(1); } size = sizeof(buffer[0]); count = sizeof(buffer) / sizeof(buffer[0]); i = fwrite(&buffer, size, count, fp); return 0; 항목 buffer size

이진 파일 읽기 size_t fread( void *buffer, size_t size, size_t count, FILE *fp );

이진 파일 읽기 count buffer size #include <stdio.h> #define SIZE 1000 int main(void) { float buffer[SIZE]; FILE *fp = NULL; size_t size; fp = fopen("binary.txt", "rb"); if( fp == NULL ) fprintf(stderr, "binary.txt 파일을 열 수 없습니다."); exit(1); } size = fread( &buffer, sizeof(float), SIZE, fp); if( size != SIZE ) fprintf(stderr, "읽기 동작 중 오류가 발생했습니다.\n"); fclose(fp); return 0; count 항목 buffer size

버퍼링 fopen()을 사용하여 파일을 열면, 버퍼가 자동으로 만들어진다. 버퍼는 파일로부터 읽고 쓰는 데이터의 임시 저장 장소로 이용되는 메모리의 블록 디스크 드라이브는 블록 단위 장치이기 때문에 블록 단위로 입출력을 해야만 가장 효율적으로 동작 1024바이트의 블록이 일반적 파일과 연결된 버퍼는 파일과 물리적인 디스크 사이의 인터페이스로 사용 파일 디스크 버퍼

버퍼링 fflush(fp); setbuf(fp, NULL); 버퍼의 내용이 디스크 파일에 써진다. setbuf()는 스트림의 버퍼를 직접 지정하는 함수로서 만약 버퍼 자리에 NULL을 써주면 버 퍼를 제거하겠다는 것을 의미한다.

예제 #define SIZE 3 struct student { int number; // 학번 char name[20]; // 이름 double gpa; // 평점 }; int main(void) { struct student table[SIZE] = { { 1, "Kim", 3.99 }, { 2, "Min", 2.68 }, { 3, "Lee", 4.01 } struct student s; FILE *fp = NULL; int i; // 이진 파일을 쓰기 모드로 연다. if( (fp = fopen("student.dat", "wb")) == NULL ) fprintf(stderr,"출력을 위한 파일을 열 수 없습니다.\n"); exit(1); }

예제 // 배열을 파일에 저장한다. fwrite(table, sizeof(struct student), SIZE, fp); fclose(fp); // 이진 파일을 읽기 모드로 연다. if( (fp = fopen("student.dat", "rb")) == NULL ) { fprintf(stderr,"입력을 위한 파일을 열 수 없습니다.\n"); exit(1); } for(i = 0;i < SIZE; i++) fread(&s, sizeof(struct student), 1, fp); printf("학번 = %d, 이름 = %s, 평점 = %f\n", s.number, s.name, s.gpa); return 0; 학번 = 1, 이름 = Kim, 평점 = 3.990000 학번 = 2, 이름 = Min, 평점 = 2.680000 학번 = 3, 이름 = Lee, 평점 = 4.010000

예제 #include <stdio.h> #include <stdlib.h> int main(void) { FILE *fp1, *fp2; char file1[100], file2[100]; char buffer[1024]; int count; printf("첫번째 파일 이름: "); scanf("%s", file1); printf("두번째 파일 이름: "); scanf("%s", file2); // 첫번째 파일을 쓰기 모드로 연다. if( (fp1 = fopen(file1, "rb")) == NULL ) fprintf(stderr,"입력을 위한 파일을 열 수 없습니다.\n"); exit(1); }

예제 // 두번째 파일을 추가 모드로 연다. if( (fp2 = fopen(file2, "ab")) == NULL ) { fprintf(stderr,"추가를 위한 파일을 열 수 없습니다.\n"); exit(1); } // 첫번째 파일을 두번째 파일 끝에 추가한다. while((count = fread(buffer, sizeof(char), 1024, fp1)) > 0) fwrite(buffer, sizeof(char), count, fp2); fclose(fp1); fclose(fp2); return 0; 첫번째 파일 이름: a.dat 두번째 파일 이름: b.dat

예제 다음과 같이 이진 파일의 내용을 16진수로 표시하는 프로그램을 작성하여 보자. 흔 히 이런 형식의 화면은 디버거에서 볼 수 있다. 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 3C 68 33 3E 0D Log</h1>..<h3>. 00000030: 0A 2D 2D 2D 2D 2D 2D 2D 2D 2D 2D 2D 2D 2D 2D 2D .---------------

예제 #include <stdio.h> #include <ctype.h> #include <stdlib.h> int main(void) { FILE *fp; char fname[100]; unsigned char buffer[16]; int address = 0; int i, bytes; printf("원본 파일 이름: "); scanf("%s", fname); if( (fp = fopen(fname, "rb")) == NULL ) fprintf(stderr,"원본 파일 %s을 열 수 없습니다.\n", fname); exit(1); }

예제 while(1) { bytes = fread(buffer, 1, 16, fp); if( bytes <= 0 ) break; printf("%08X: ", address); for(i = 0; i < bytes; i++) printf("%02X ", buffer[i]); if( isprint(buffer[i]) ) putchar(buffer[i]); else putchar('.'); address += bytes; putchar('\n'); } fclose(fp); return 0;

임의 접근 파일 순차 접근(sequential access) 방법: 데이터를 파일의 처음부터 순차적으로 읽거 나 기록하는 방법 임의 접근(random access) 방법: 파일의 어느 위치에서든지 읽기와 쓰기가 가능 한 방법 순차접근파일 임의접근파일

임의 접근 파일의 원리 파일 위치 표시자 파일 위치 표시자: 읽기와 쓰기 동작이 현재 어떤 위치에서 이루어지는 지를 나타낸다. 강제적으로 파일 위치 표시자를 이동시키면 임의 접근이 가능 파일 위치 표시자

임의 접근 관련 함수 int fseek(FILE *fp, long offset, int origin); 상수 값 설명 SEEK_SET 파일의 시작 SEEK_CUR 1 현재 위치 SEEK_END 2 파일의 끝 fseek(fp, 0L, SEEK_SET); // 파일의 처음으로 이동 fseek(fp, 0L, SEEK_END); // 파일의 끝으로 이동 fseek(fp, 100L, SEEK_SET); // 파일의 처음에서 100바이트 이동 fseek(fp, 50L, SEEK_CUR); // 현재 위치에서 50바이트 이동 fseek(fp, -20L, SEEK_END); // 파일의 끝에서 20바이트 앞으로 이동 fseek(fp, sizeof(struct element), SEEK_SET); // 구조체만큼 앞으로 이동

파일 위치 표시자를 초기화하고 현재 위치를 알아내는 함수들입니다. 임의 접근 관련 함수 파일 위치 표시자를 0으로 초기화 void rewind(FILE *fp); 파일 위치 표시자의 현재 위치를 반환 파일 위치 표시자를 초기화하고 현재 위치를 알아내는 함수들입니다. long ftell(FILE *fp);

예제 #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); }

예제 // 배열을 이진 모드로 파일에 저장한다. 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); return 0;

예제 파일에서의 위치를 입력하십시요(0에서 999, 종료 -1): 3 3 위치의 값은 9입니다. // 배열을 인덱스의 제곱으로 채운다. void init_table(int table[], int size) { int i; for(i = 0; i < size; i++) table[i] = i * i; } 파일에서의 위치를 입력하십시요(0에서 999, 종료 -1): 3 3 위치의 값은 9입니다. 파일에서의 위치를 입력하십시요(0에서 999, 종료 -1): 9 9 위치의 값은 81입니다. 파일에서의 위치를 입력하십시요(0에서 999, 종료 -1): -1

중간 점검 1. 파일의 처음부터 순차적으로 읽거나 쓰는 방법을 _____이라고 한다. 2. 파일의 어느 위치에서나 읽고 쓰기가 가능한 방법을 ________이라고 한다. 3. 파일에서 읽기나 쓰기가 수행되면 파일의 현재의 위치를 표시하는 ___________가 갱 신된다. 4. 파일의 위치 표시자를 알아내는 함수는 _____이다.

실습: 주소록 만들기 자신과 친한 사람들의 정보를 저장하고 업데이트할 수 있는 간단한 프로그램을 작 성하여 보자. 입력하거나 업데이트한 데이터는 파일로 저장된다. 저장된 데이터에 대하여 검색할 수 있다. 자기에게 필요한 여러 가지 사항들을 저장할 수 있도록 하자. 즉 자신만의 간단한 데 이터베이스 시스템을 작성하여 보자.

실행 결과 ===================== 1. 추가 2. 수정 3. 검색 4. 종료 메뉴를 선택하세요: 1 이름: 홍길동 주소: 서울시 종로구 1번지 휴대폰: 010-123-4567 특징: 싸움을 잘함, 변신에 능함 ...

힌트 파일 모드를 어떻게 해야 할까? 적합한 것은 “a+"모드이다. 주로 추가, 탐색 할 예정 파일에서 읽기 전에 무조건 fseek()를 해주어야 한다. 수정할 때는 차라리 새로운 파일을 생성하여서 거기에 전체를 다시 기록하는 것이 낫다.

예제 #include <stdio.h> #include <string.h> #define SIZE 100 typedef struct person { // 연락처를 구조체로 표현한다. char name[SIZE]; // 이름 char address[SIZE]; // 주소 char mobilephone[SIZE]; // 휴대폰 char desc[SIZE]; // 특징 } PERSON; void menu(); PERSON get_record(); void print_record(PERSON data); void add_record(FILE *fp); void search_record(FILE *fp); void update_record(FILE *fp);

예제 int main(void) { FILE *fp; int select; // 이진 파일을 추가 모드로 오픈한다. if( (fp = fopen("address.dat", "a+")) == NULL ) { fprintf(stderr,"입력을 위한 파일을 열 수 없습니다); exit(1); } while(1) { menu(); // 메뉴를 표시한다 printf("정수값을 입력하시오: "); // 사용자로부터 정수를 받는다 scanf("%d",&select); switch(select) { case 1: add_record(fp); break; // 데이터를 추가한다 case 2: update_record(fp); break; // 데이터를 수정한다 case 3: search_record(fp); break; // 데이터를 탐색한다 case 4: return 0; fclose(fp); // 이진 파일을 닫는다 return 0;

예제 // 사용자로부터 데이터를 받아서 구조체로 반환한다 PERSON get_record() { PERSON data; fflush(stdin); // 표준 입력의 버퍼를 비운다 printf("이름); gets(data.name); // 이름을 입력받는다 printf("주소); gets(data.address); // 주소를 입력받는다 printf("휴대폰); gets(data.mobilephone); // 휴대폰 번호를 입력받는다 printf("특징); gets(data.desc); // 특징을 입력 받는다 return data; } // 구조체 데이터를 화면에 출력한다. void print_record(PERSON data) printf("이름\n", data.name); printf("주소\n", data.address); printf("휴대폰\n", data.mobilephone); printf("특징\n", data.desc);

예제 // 메뉴를 화면에 표시하는 함수 void menu() { printf("====================\n"); printf(" 1. 추가\n 2. 수정\n 3. 검색\n 4. 종료\n"); } // 데이터를 추가한다 void add_record(FILE *fp) PERSON data; data = get_record(); // 사용자로부터 데이터를 받아서 구조체에 저장 fseek(fp, 0, SEEK_END); // 파일의 끝으로 간다 fwrite(&data, sizeof(data), 1, fp); // 구조체 데이터를 파일에 쓴다

예제 // 데이터를 탐색한다 void search_record(FILE *fp) { char name[SIZE]; PERSON data; fseek(fp, 0, SEEK_SET); // 파일의 처음으로 간다 fflush(stdin); printf("탐색하고자 하는 사람의 이름"); gets(name); // 이름을 입력받는다 while(!feof(fp)){ // 파일의 끝까지 반복한다 fread(&data, sizeof(data), 1, fp); // 현재 위치에서 데이터를 읽는다 if( strcmp(data.name, name) == 0 ){ // 이름을 비교한다 print_record(data); // 일치하면 데이터를 화면에 출력한다 break; } // 데이터를 수정한다 void update_record(FILE *fp) //...

도전문제 search_record()에서 탐색이 실패했으면 에러 메시지를 출력하도록 코드를 추가하 여 보라. update_record() 함수를 구현하여 보자. 앞에서 언급한 대로 수정된 부분만 파일 에 덮어쓰는 것은 상당히 어렵다. 따라서 수정된 전체 내용을 읽어서 새로운 파일에 쓰도록 해보자.

Q & A