본문 바로가기

Today I Learned

23.02.01 - 스트림, 콘솔 입출력, 파일 입출력

스트림

스트림은 흐름을 의미한다. 물이 흘러가듯이 바이트들이 순서대로 입출력되는 논리적인 장치를 스트림이라고 한다.

스트림은 단방향성을 띄기 때문에 입력, 출력 스트림이 따로 존재하고 내부적으로 버퍼를 가지고 있다.

버퍼는 임시적으로 데이터를 저장하고 컴퓨터는 버퍼에 데이터를 저장해놨다가 한꺼번에 처리한다.

 

콘솔 표준 입출력 스트림

stdin : 표준 입력 스트림

stdout : 표준 출력 스트림

stderr : 표준 에러 스트림

 

단일 문자 출력 함수 - putchar, fputc

putchar(65);		//콘솔 입출력만 사용 가능
fputc('A',stdout);	//모든 입출력에 사용 가능 (파일 입출력도 가능)

단일 문자 입력 함수 - getchar, fgetc

int ch = getchar();
int ch = fgetc(stdin);

EOF(end of file) - 파일의 끝을 표현하기 위해 정의한 상수 (-1)

while (true)
{
	char ch = getchar();

	if (EOF == ch)			// 콘솔 입출력 시 단축키 ctrl + z는 eof에 해당
		break;

	putchar(ch);
}

위 코드는 Ctrl + Z를 입력받기 전까지 문자를 입력받고 출력하는 것을 반복한다.

 

문자열 출력 함수 - puts, fputs

puts("hello"); // 자동 개행 지원
fputs("hello",stdout); // 자동 개행 X

자동개행을 지원하는 puts에 비해 fputs는 자동개행을 지원하지 않는다.

자동개행은 줄을 바꿔주는 라인피드(\n)를 하면서 동시에 커서를 가장 앞으로 보내는 캐리지 리턴(\r)을 한다.

fputs는 텍스트 모드 뿐만 아니라 바이너리 모드도 지원하기 때문에 텍스트가 아닌 이진 데이터를

라인피드와 캐리지 리턴을 한다면 데이터의 오류가 발생할 수 있기 때문이다.

 

문자열 입력함수 - gets_s, fgets

char szInput[5] = "";

gets_s(szInput, sizeof(szInput));	//공백을 읽어들이고 엔터는 제외,사이즈가 초과되면 오류 발생
fgets(szInput, sizeof(szInput), stdin); // 설정한 사이즈만큼 읽어오고, '\0'(엔터)문자를 고려한다;

 

 

 

파일 입출력 과정

스트림 생성 -> 파일 쓰기 / 파일 읽기 -> 스트림 소멸

 

절대 경로 : 드라이브명~ 현재 파일 위치까지 모두 표기

상대 경로 : 프로젝트 파일 위치 기준으로 현재 파일 위치 표기

 

텍스트 모드 입출력

	FILE*	pSaveFile = nullptr;

	errno_t	err = fopen_s(&pSaveFile, "../Data/Test.txt", "wt");

	if (0 == err)	// 파일 개방 성공
	{
		fputs("hello world", pSaveFile);

		cout << "저장 성공!" << endl;
		
		fclose(pSaveFile);		// 파일 개방시 반드시 소멸시켜줘야 함
	}
	else
		cout << "파일 개방 실패" << endl;
	
	FILE*	pLoadFile = nullptr;

	errno_t	err = fopen_s(&pLoadFile, "../Data/Test.txt", "rt");

	char		szTemp[32] = "";

	if (0 == err)	// 파일 개방 성공
	{
		fgets(szTemp, sizeof(szTemp), pLoadFile);
		fputs(szTemp, stdout);

		cout << "불러오기 성공!" << endl;

		fclose(pLoadFile);		// 파일 개방시 반드시 소멸시켜줘야 함
	}
	else
		cout << "파일 개방 실패" << endl;

fopen_s는 스트림을 생성 (파일을 개방) 하는 함수이다.

errno_t fopen_s(FILE** _Stream, char const* _FileName, char const* _Mode);

마지막 인자인 Mode를 통해 어떤 스트림을 생성할지 모드를 설정할 수 있다.

출처: soen.kr

첫번째 글자는 이 세가지가 있고, 두번째 글자에는 t가 오면 텍스트 파일, b가 오면 이진 파일을 의미한다.

r+, w+, a+도 있지만 c++에서 없어졌으므로 있다는 것만 알아두자.

위 코드는 텍스트 파일을 쓰는 용도일 때 wt, 읽는 용도일 때 rt를 이용했다.

 

 

 

바이너리 모드 입출력

size_t fwrite(void const* _Buffer, size_t _ElementSize, size_t _ElementCount, FILE*  _Stream);
	//(출력할 메모리의 시작 주소, 출력할 메모리의 사이즈, 출력할 메모리의 개수, 개방한 스트림)
 size_t fread(void const* _Buffer, size_t _ElementSize, size_t _ElementCount, FILE*  _Stream);

이진 파일에 데이터를 쓰고 읽을 때는 fgets, fputs가 아닌 fwrite, fread를 사용한다.

	int		iArray[5] = { 1, 2, 3, 4, 5 };
	
	FILE*	pSaveFile = nullptr;

	errno_t	err = fopen_s(&pSaveFile, "../Data/Array.txt", "wb");

	if (0 == err)	// 파일 개방 성공
	{
	
		fwrite(iArray, sizeof(iArray), 1, pSaveFile);
		//fwrite(iArray, sizeof(int), 5, pSaveFile);

		cout << "저장 성공!" << endl;

		fclose(pSaveFile);		// 파일 개방시 반드시 소멸시켜줘야 함
	}
	else
		cout << "파일 개방 실패" << endl;

	int		iTemp[5] = {};

	FILE*	pLoadFile = nullptr;

	errno_t	err = fopen_s(&pLoadFile, "../Data/Array.txt", "rb");

	if (0 == err)	// 파일 개방 성공
	{

		//fread(iTemp, sizeof(iTemp), 1, pLoadFile);
		fread(iTemp, sizeof(int), 5, pLoadFile);

		cout << "불러오기 성공!" << endl;

		fclose(pLoadFile);		// 파일 개방시 반드시 소멸시켜줘야 함
	}
	else
		cout << "파일 개방 실패" << endl;


	for (int i = 0; i < 5; ++i)
	{
		cout << iTemp[i] << endl;
	}