본문 바로가기

별걸다하는 IT/프로그래밍언어

[C언어] 파일입출력 -스트림(STREAM)에 대한 이해, fopen, fclose

[C/ C++ 프로그래밍 기초 목차]

오랜만에 사용해보는 기본스티커 ㅎㅎㅎ

안녕하세요. 즐거운(?) 주말이예요 ㅎㅎ 오늘은 파일입출력에 관한 포스팅을 들고 왔숩니다.

오랜만에 옛날에 배운걸 더듬더듬하려다 보니까 속도가 늦네요 ㅎㅎ

 

파일입출력 필요성

여러분은 여지껏(?) 지금 강좌에서는 콘솔창에서 입력받고 출력하고 했어요 ㅎㅎ

그런데 이런 결과들은 프로그램을 종료하면, 사라져요.ㅎㅎ 왜냐면 콘솔창에 뜨는 데이터들은 RAM이라고 휘발성 메모리에 저장되는 거기 때문이죠. 

사진출처: http://pricecoupongb.ga/gudu/file-handling-c-program-2160.php

한 번쯤, 이런 것들을 파일로 기록해서 가지고 있고 싶다. 생각해본적 없으신가요??

또는 일일이 콘솔에다가 입력하기보다는, 파일 같은 곳의 정보들을 한 번에 쫙 읽어들여서 수행시키고 싶을 때도 있어요.

 

예를 들어서,

이런 데이터를 일일이 프로그램에다가 다시 입력하려니까 너무 힘들어요!!

엑셀이던, 메모장이던, 한글이던, 어떤 파일을 읽어들여서, 자동으로 반평균, 반최고점, 전체최고점, 전체등수 드응을 계산하고 싶을거예요. 그리고 기껏 계산한 결과를, 어딘가에 입력해두지 않고 꺼버리게 되면,,, 으아아ㅏ 날라가버리죠! 얼마나 불편합니까! 이런 것들도 자동으로 얘네가 파일로 기록해줬으면 좋겠어요~~

파일입력과 파일출력은 이렇게나 편의성을 높여주면서도 없어서는 안될 중요한 기능입니다.

 

일단 실습해보기 - 파일 출력해보기 [C언어]

#include <stdio.h>
int main(void)
{
	FILE *fp = fopen("hello.txt", "w");
	fputs("hello world", fp);
    	fclose(fp);
	return 0;
}

요렇게 적어주고 실행시켜주세요. 그럼 콘솔창에는 아무것도 안뜰거예요!!

대신 프로젝트가 저장된 공간으로 가서 아래처럼 프로젝트 폴더에 들어가보면

사진 설명을 입력하세요.

hello.txt라는 파일에 hello world라고 작성이 되어 있을거예요!

fopen의 역할?

FILE *fp = fopen("hello.txt", "w");
fputs("hello world", fp);

가장 핵심이 될 것 같은 코드를 보면, 뭔지 모르겠지만 fopen은 파일 오픈의 약자 같아요 그죠?

그리고 첫 인자로 문자열을 받았는데 이 문자열 이름의 파일(여기선 hello.txt)이 생성되었네요. 그리고 fopen의 반환값(fp)을 fputs 인자로 넣어주자, 그 파일에 원하는 글씨를 써줬어요! fputs에 파일 이름을 안써줘도 해당 파일에 글씨를 써줬다는건 fp가 그 파일에 대한 정보를 갖고 있다고 해석할 수 있겠네요.

이것만 봤을 때, fopen("hello.txt", "w") 이 코드는 대략 hello.txt 파일로 통하는 연결고리 역할을 해주는 것 같아요.

 

맞습니당~~~ fopen은 내가 지정한 파일과 소통할 수 있도록 스트림STREAM을 생성해줍니다.

그렇다면 스트림은 무엇일까요??

 

스트림(STREAM)이란?!

사실 파일과 우리가 돌리는 프로그램은 같은 선상에 있는게 아니예요. 무슨 말이냐~

우리가 컴퓨터에 SSD를 하나 꼽고, 하드디스크도 하나 꼽았다고 합시다.

만약 비주얼 스튜디오에서 만든 프로그램이, C드라이브에 저장되어 있다고 해봐요.

근데 만약 내가 읽고 싶은 파일은 D드라이브에 있어!!! 그럼 당연히 물리적으로 다른 위치에 있는 거잖아요??

 

우리야 뭐 쉽게 마우스 드래그로 하드디스크에 저장되어 있는 파일들을 자유롭게 옮기기도 하고, 우클릭 새로만들기로 쉽게 파일을 작성하기도 하지만, 사실 우리가 이렇게 쉽게 쓸 수 있도록 편리하게 포장되어 있는 것일뿐, 그 내부에서는 많은 일들이 수행된답니다. 우리가 일일이 수고하지 않게 다행이 이런 일들은 우리 운영체제가 수행해줘요.

운영체제한테 우리가, "야 내가 어떤 특정 파일을 읽거나 파일을 새로 만들어서 뭔가 쓰고 싶은데, 그렇게 할 수 있게 필요한 로직을 좀 처리해줘!!" 이렇게 운영체제한테 명령시키는 함수가 fopen이고, 우리는 이러한 상태를 프로그램과 파일위치 상 소통할 수 있도록 다리가 놓였졌다! 라고 해서 스트림이라고 표현합니다.

 

이렇게 운영체제가 스트림을 형성해줌으로써, 우리는 그 통로로 데이터를 단순히 보내기만 하면 끝인거죠! 물론 데이터를 받고 싶으면 데이터 받는 스트림 하나 만들어줘! 요청한 다음에 거기서 보내주는 데이터드를 읽기만 하면 되는거예요. 둘 사이 소통을 위한 전처리들을 우리가 할 필요가 없어요 ㅎㅎ

 

조금더 자세히 그림을 그려봤습니다. fopen은 운영체제에게 스트림을 준비해달라고 명령하는 함수라고 했죵?

내가 어떤 파일과 연결하고 싶은지 등의 정보를 담아요.

그럼 프로그램이 한 줄 한 줄 수행되다가 이 라인에서 해당 함수를 수행하게 되면, 운영체제가 이제 그 파일과 통신할 수 있도록 준비하고 그 통신하는데 필요한 정보를 파일구조체에다가 담아서 반환해줍니다 (스트림 정보를 반환했다고 해요)

반환하는 FILE 구조체 멤버에 직접적으로 접근할 일이 없기 때문에 어떻게 정의되어 있는지 알 필요는 없습니당. 다만 이러한 포인터로 우리가 파일과 연결되 접근할 수 있다! 라고 이해하면 됩니다.

 

표준 스트림(Standard Stream)이란?

이 사진을 보면, 콘솔이나 키보드도 스트림을 통한다고 되어 있습니다.

근데 사실 우리는 printf 함수를 이용해서 여지껏 모니터에 출력하고,

scanf함수를 이용해서 키보드에서 입력을 받았는데,,,,, fopen함수라던가 스트림 연결을 위한 별도의 코드를 짠 적이 없잖아요!?

 

사실 모니터와 키보드는 표준스트림이라고 해서 프로그램이 실행될 때 자동으로 생성됩니다.

그래서 우리가 여지껏 작업할 때에는 스트림에 대한걸 몰랐던 거예요 ㅎㅎ

참고로, 모니터에 대한 스트림은 stdout이고 (우리가 파일 구조체 반환받은 걸 fp라고 지정해줬었는데, 자동으로 해주는 표준스트림에서는 모니터 스트림을 stdout에 저장합니다) 키보드에 대한 스트림은 stdin이라고 합니다. 

stderr이라고 해서 오류 스트림이라는 것도 있습니다. ㅎㅎ

 

다시 보는 fopen과 fclose, 그리고 모드(mode)

fopen함수

이제 스트림에 대한 전반적인 이해는 됐을거라 생각합니다!! 다시 돌아와서 fopen에 대해서 좀 더 살펴볼까요?!

#include <stdio.h>
FILE *fopen (const char* filename, const char* mode);

fopen함수는 stdio.h라는 라이브러리에 정의되어 있습니다.

성공시 해당 파일의 FILE 구조체 변수의 주소 값, 실패시 NULL 포인터를 반환합니다.

첫 인자로는 파일이름을 받고, 두 번째 인자로는 모드를 받습니다.

 

▶만약 파일의 위치를 지정하고 싶다면?

아래코드처럼 filename에 위치까지 함께 넣어주면 됩니다.

fopen("E:\\program\\abc.txt", "w");

 

모드는 무엇인가!?

파일로부터 입력만 받을거면 입력모드로 스트림을 생성하는거고!!

출력만 받을거면 출력모드로 스트림을 생성하는거고!!

스트림은 '한 방향으로 흐르는 데이터의 흐름'을 의미합니다. 그래서 형성하고자 하는 스트림의 종류를 지정해줘야 하는데 그게 모드인거죠!

 

모드(mode)

스트림의 성격

파일이 없으면?

r (read의 약자)

읽기 가능

에러

w (write의 약자)

쓰기 가능

생성

a

파일의 끝에 덧붙여 쓰기 가능

생성

r+

읽기/쓰기 가능

에러

w+

읽기/쓰기 가능

생성

a+

읽기/덧붙여 쓰기 가능

생성

rb, wb, ab

뒤에 b가 붙으면 이진파일을 연다는거

(각, r,w,a속성에 따름)

rt, wt, at

텍스트 데이터를 쓰기 위한 스트림

(각, r,w,a속성에 따름)

예측했겠지만, 뒤에 +가 붙으면 읽기, 쓰기가 모두 가능한 스트림을 형성하게 됩니다.

 

▶스트림은 한방향아닌가요? 양방향되면 양방향쓰지 굳이 +붙은거(r+,w+,a+) 안쓰고 r,w,a 쓰는 이유가 무엇인가요?

이렇게 생각할 수도 있습니다!! 예 스트림은 한쪽으로 흘러가는게 맞아요 그래서 사실 +가 붙는 모드는 양방향이라기보다는 꼼수(?)가 안에 있는거예요. 인풋과 아웃풋이 동시에 이루어지지는 않습니다.

예를 들어, a+의 경우, 시작할 때 파일의 시작지점을 가리키는데, 만약 쓰려는 행위가 시도되면 파일 맨 뒤로 위치가 이동됩니다. (이런식인거예요..ㅎ) 그래서 다시 원하는 부분부터 읽으려면 fseek이나 rewind 함수 등을 이용해서 사용자가 위치를 지정해줘야해요.

 

즉 양방향 작업을 하기 위해서는 메모리의 버퍼를 비워줘야 하거나 저런 함수를 사용해야 하는 등 여러 불편함이 있습니다. (ex. fflush, fseek, fsetpos, rewind 등등)

작업을 변경할 때마다, 불편함에 있고, 잘못된 사용의 위험성도 따르므로 r,w,a 중에서 하나를 선택해서 스트림을 형성하는게 좋습니다.

 

fclose함수

스트림을 이용해서 파일을 읽거나 쓰거나 하는 조작을 맞췄으면 fclose를 이용해서 스트림 소멸을 요청해야 합니다! fclose는 운영체제가 할당한 자원을 반환하거나 버퍼링 되었던 데이터를 출력해주는 역할을 합니다. 이렇게 해야 자원손실을 초래하지 않고, 파일이 개방된 상태에서 프로그램이 종료되었을 떄 일어나는 파일 손상을 막아주기 때문이야요 

 

파일을 열었으면, 파일에다가 글을 쓰거나 파일로부터 글을 읽어서 활용하거나 해야겠죠?

이렇게 파일관련되어 읽거나 쓰는데 사용하는 함수가 있는데요. 해당 글은 아래 링크를 참고해주시면 됩니다.

▼파일입출력 함수: jhnyang.tistory.com/199

 

[C/C++] 파일 입출력 함수, 파일 읽는 함수 fgets & fgetc 함수 알아보기, 파일 문자열 컨트롤 함수 stdio

[C/ C++ 포스팅 링크 모음 목차] 안녕하세요! 저번에 파일 입출력의 시작을 알리는 스트림에 대해서 알아봤어요 ㅎㅎ 지난번 포스팅이 궁금하다면 아래 링크를 참고하세요 [C언어] 파일입출력 1편-

jhnyang.tistory.com

 

으앙 ㅠ 벌써 시간이 이렇게 지났네요 ㅎㅎ 은근 포스팅 하나 쓰는데 잡아먹는 시간이 ㅎㄷㄷㄷ

열심히 썼지만, 오타나 잘못된 부문이 있을 수도 있어요. 피드백은 항상 좋습니다 ♥

도움이 되셨다면, 댓글, 공감, 보답광고 중 하나는 우떤가요?! 다음 포스팅에는 버퍼에 대해서, 그리고 입력 출력에 대해서 좀 더 알아보도록 합시다.