15장
데이터가 파일에 들어가 있고, 결과물을 화면이 아닌 파일로 보냄. 파일에 데이터가 있는 이유는 많은 양의 데이터가 있기 때문. 데이터 입력후 컴파일을 하면 다시 입력을 해야 됨. 지뢰 찾기의 경우 일일히 입력을 해야하기 때문에 그걸 파일에 저장하고 그대로 갖고 옴. 파일 입출력과 관련하여 배우자.
스트림 : 바이트의 흐름
컴퓨터 - 키보드, 마우스, 스캐너, 마이크, 음성, 터치 등등의 입력장치 존재
키보드를 통해, 마우스를 통해, 마이크를 통해 입력이 되면 입력 장치에 따른 함수를 만들어야 함. 그럴 때마다 값들을 새로운 입력장치가 있으면 이를 받아들이기 위해 다른 코드가 등장해야함.
출력장치도 마찬가지임.
그러면 입력장치와 출력장치가 계속 바뀌게 되어서 의존성이 높아짐. 이 의존성을 낮추기 위해 한 가지 길로만 들어오게 함.
키보드, 마우스, 스캐너, 마이크, 음성, 터치 ---바이트의 흐름 ---> 하나의 입력장치
바이트가 흘러갈 수 있도록, 받을 수 있도록 코드를 제공하고, 각각의 입력장치에 따른 코드는 별도로 받아옴. 출력도 상황에 따라 각각 내보내는 것이 아니라 바이트 단위로 내보내려고 하면 알아서 장치에 디스플레이 됨.
하나의 스트림은 구조체에 의해 정의됨. 구조체의 멤버가 파일 입출력에 대한 건 알지 않아도 되고, 스트림으로 입력하고 출력할 것이면 파일 구조체를 쓰면 됨. 파일이라는 구조체를 사용하여 파일 입출력을 함. 컴퓨터로 a.txt를 읽은 뒤 파일 구조체에 저장하면 됨. 그리고 출력할 때도 결과를 출력하기 위해 특정 파일로 내보낼 건데, 파일 구조체 형식으로 되어진 변수로 내보낸다고 하면 됨. 파일 입출력은 스트림으로 다룬다.
변수 스트림 (입력) ---------> 변수 스트림 (출력)
스트림과 FILE
• 스트림(stream): 입력과 출력을 바이트(byte)들의 흐름으로 생각
• 스트림은 구체적으로 FILE 구조체를 통하여 구현
• FILE은 stdio.h에 정의되어 있다.
스트림은 파일 구조체로 구형함. FILE이라는 구조체임. 이미 stodio.h에 정의되어 있음.
표준 입출력 스트림(standard input/output stream): 필수적 몇 개의 스트림
• stdin: 표준 입력 스트림(키보드)
• stdout: 표준 출력 스트링(모니터의 화면)
• stderr: 표준 에러 스트림(모니터의 화면)
스트림의 용도가 있음. 들어오는 것이 스트림인데, 키보드로부터 들어오는 스트림이 있다고 가정하고, 나갈 때는 스트림으로 나가는데 모니터로 나가는 스트림이 있을 것임. 스트림 중에서도 키보드로 들어오는 스트림을 표준 입력 스트림, 모니터로 나가는 스트림을 표준 출력 스트림이라고 함. 만약 별도의 스트림을 말하지 않으면 키보드와 모니터라고 생각하면 된다.
그리고 stderr는에러가 나가는 길임. 에러 코드를 내보낼 때 이 스트림으로 내보냄. 장소는 모니터임. std가 붙은 건 모니터나 키보드임.
표준이 아니라 다른 입출력 스트림을 사용하고 싶다면.. 파일을 사용한다. 파일로부터 들어오고 파일로 나가기 원함. 지금까지 기본은 키보드와 모니터인데, 들어오는 길이 다름.
파일
• 파일이 필요한 이유: 데이터 보존
• 텍스트 파일 vs 이진 파일(사람은 못읽고 컴퓨터는 읽음. Ex 실행파일)
• 파일 처리
파일 열기 -> 파일 읽기, 쓰기 -> 파일 닫기
파일은 보관되어 있는 데이터를 입력하고 싶고, 결과를 파일에 저장하고 싶음. 텍스트 파일은 우리가 읽을 수 잇음. 한글, 아라비아 숫자, 영어로 쓰여져 잇음. 그런데 이진 파일은 01로 적혀있음. 이미지, 사운드의 경우에는 이걸로 받음. 컴퓨터는 디지털기기이므로 모두 숫자로 표현되어야 함. 사진이나 사운드는 숫자로 변환하고 이진수로 변환되어진 값을 받아서 처리함. 그리고 모니터로 보여질 때 이미지로 변해져 나옴. 파일은 메모장이나 비주얼 스튜디오 코드에서 저장한 형태가 되고, 이미지나 사운드나 영상은 이진 파일을 사용함.
파일을 사용하려면 scanf를 사용함. 표준입력스트림값을 처리함. printf는 표준 출력스트림을 내보냄. 그런데 표준이 아닌 다른 것을 사용하고 싶다면, 파일은 열어놔야 사용할 수 있음. 그 다음 파일에 있는 값을 읽고, 파일을 닫아야 끝남. 파일을 안 쓰고 싶으면 꼭 닫아야 함. 그래야 다른 프로세스가 사용됨. 내가 특정 파일에 쓰고 싶다면 그 파일을 열고 그 파일에 쓴 뒤, 그 파일을 닫아야 쓰기가 완성됨. 닫지 않으면 쓰여지지 않음.
*실기 close를 안해서 틀린 사람이 개많았음. 유의하자. 파일을 사용하려면 열기, 읽거나 쓰기, 닫기를 기억하자
파일 포인터의 선언
FILE *fp; // 파일을 가리킬 포인터 변수의 선언
파일에 의해 변수가 만들어지는데, 입력용 파일을 가리킬 수도 있고 출력용 파일을 가리킬 수도 잇음. 그래서
fp는 파일을 가리키고 있음.
파일 열기: fopen
원형: FILE *fopen(const char *filename, const char * mode);
기능: 파일을 열고 성공시 해당 파일의 FILE 구조체 변수의 주소값, 실패 시 NULL 포인터를 반환한다.
파일에서 데이터를 읽거나 쓸 수있도록 모든 준비
FILE *fp;
FILE *fp1, *fp2; // 별표를 각자 써야 함.
fp1 = fopen("hello.txt", "rt"); // fp1은 입력 파일 포인터 -> r로 써도 되고 rt로 써도 됨. rt는 텍스트 파일이라는 뜻. 그런데 r은 원래 텍스트 파일을 받아오기 때문에 r만 써도 됨. 만약 바이너리 파일을 받아오고 싶다면 rb라고 쓰면 됨. 그
if (fp1 == NULL) // hello 파일이 열어야 함. 그래서 이 함수를 써야 함. 포인터가 가리키는 주소가 없다면, 파일이 업ㅄ다는 뜻임. 파일의 경로가 잘못되어서 못 찾았을 수 있음. 그래서 fp1을 열었는데 그 파일이 NULL이라면 열지 못했다는 뜻임. fp1은 입력파일 포인터임. 입력 파일을 가리키는 포인터고 타입은 FILE이라는 구조체 타임임.
{
printf("파일 오픈 에러… !!!\n");
return 1; //return 1이 되면 main이 중지됨.
}
열고 싶은 파일명을 씀 const char *filename
읽기 용인지 쓰기용인지 추가용인지 알면 됨. const char * mode
읽기 - r 쓰기 - w 추가 - a (쓰는 건데, 추가형태임. w는 처음부터 쓰는데 a는 마지막에 씀) 만약 같은 파일이 있는데 여기에 w를 쓰면 기존 내용이 지워지고 처음부터 쓰여지고 a는 기존의 것이 지워지지 않고 뒤에 붙어 들어감)
예를 들어 a파일을 열고 싶다면
FILE* fp = dopen("a.txt", "r"); //읽기용 -> 가리켜야 하기 때문에 그 값을 저장할 변수를 써야 함. 그런데 fp는 구조체 변수임. 그리고 fp는 FILE의 구조체 포인터 변수임. a.txt 를 스트림 단위로 읽고 fp가 가리키는 것임.
파일을 열고 성공시 : 파일이 있어서 열었음 -> 파일의 구조체 변수의 주소값이 들어감
실패시 : 파일이 없어서 못 열었음. -> 파일의 NULL이 리턴됨.
파일에 읽기, 쓰기
입력 파일 포인터에 stdin을 쓰면? 키보드에서 입력을 받음
출력 파인 포인터에 stdout를 쓰면?
FILE *fp;
FILE *fp1, *fp2; // 별표를 각자 써야 함.
fp1 = fopen("hello.txt", "rt");
fp2 = fopen("output.txt", "w"); //쓰기용 파일은 자동으로 만들어짐. 그래서 파일이 있는지 없는지 물어볼 필요 없음. 읽기용과 다르게.
fgetc -> f 파일로부터 데이터를 하나만 읽어와서 char에 저장했다는 것임. <stdio.h>
getc도 가능함. 읽을 때 f를 쓰는 게 더 예쁘긴 함.
fputc(ch, fp2); // fp2도 열어야함.
putc(ch, fp2);
(놓침)
char buf[30];
fgets(buf, sizeof(buf), fp1); //문자열 단위로 파일에서 읽기
fputs(buf, fp2); //문자열 단위로 파일에서 쓰기
fgets(buf, sizeof(buf), fp1); //문자열 단위로 파일의 끝까지 읽어서 쓰기
while(!feof(fp1)) {
fputs(buf, fp2);
fgets(buf, sizeof(buf), fp1);
}
그 내용을 저장하여 읽어온 후, 그 파일이 끝까지 안 갔으면 읽으면 됨. 그래서 복붙하면 된다.
txt 파일을 읽어올 때는 문자열 단위로 읽을 때는 \n이어야 하므로 꼭 줄바꿈을 해주어야 함.
3. 데이터 타입을 지정한 입출력
입력 출력
fscanf(fp, "형식지정자", &변수); fprintf(fp, "형식지정자", 변수);
fscanf(fp1, "%d", &num); //파일내의 숫자 num읽기
fprintf(fp1, "%d", num); //파일 내의 숫자 num 쓰기
//데이터 타입을 지정하면서 파일의 끝까지 읽어서 쓰기
fscanf(fp1, "%d", &score);
while(!feof(fp1)) {
fprintf(fp2, "%d\n", score + 9);
fscanf(fp1, "%d", &score);
}
반복문이 돌 때 마다 fp가 빈줄 null까지 와서 반복문이 끝난 것임.그래서 fp는 그대로 NUll에 있는 것임.
그래서 fseek를 사용함.
파일의 임의 접근: fseek을 이용한 파일위치표지자 조작
파일위치표지자는 파일을 오픈할 때, 읽을때, 쓸때 이동한다.
아래처럼 파일위치표지자의 위치를 이동할 수있다.
fseek(fp, 0, SEEK_SET); // 파일의 처음으로 이동
fseek(fp, 0, SEEK_END); // 파일의 끝으로 이동
ㄹㄴ
C언어