[C++] 7.2 C++ 파일 입출력 - ifstream, ofstream, stringstream

Sireal·2022년 3월 11일
1

fstream

파일 스트림은 istream, ostream 클래스 보다 지원기능이 더 많고 이를 상속해서 사용한다.
그래서 이름이 ifstream, ofstream 이 됐다.

#include <fstream>
#include <iostream>
#include <string>

int main(){
	// 파일 읽기 준비
	std::ifstream in("test.txt");
	std::string s;

	if(in.is_open()){
		in >> s;
		std::cout<<"입력 받은 문자열 ::"<< s<< std::endl;
	} else{
		std::cout<<"파일이.. 없잖아!!!" << std::endl;
	}
	return 0;
}

std::ifstream in("test.txt"); : 읽을 파일의 경로를 지정한다.
in.is_open() : 파일이 열렸는지 유무 확인가능
in >> s; : in 객체를 사용해 파일을 읽을 수 있다.

ifstream 에서는 자동 fclose를 해주기때문에 close를 꼭 안해줘도 된다.

근데 close를 해야하는 경우가 있다.

  • 여러 파일을 열었다 닫았다 해야하는 경우
#include <fstream>
#include <iostream>
#include <string>

int main(){
	// 파일 읽기 준비
	std::ifstream in("test.txt");
	std::string s;

	if(in.is_open()){
		in >> s;
		std::cout<<"입력 받은 문자열 ::"<< s<< std::endl;
	} else{
		std::cout<<"파일이.. 없잖아!!!" << std::endl;
	}

	in.close();

	in.open("other.txt");

	if(in.is_open()){
		in >> s;
		std::cout<<"입력 받은 문자열 ::"<< s<< std::endl;
	} else{
		std::cout<<"파일이.. 없잖아!!!" << std::endl;
	}

	return 0;
}

  • 다른파일을 사용하기위해서는 기존에 사용하던 파일은 무조건 close 해줘야함.
#include <fstream>
#include <iostream>
#include <string>

int main(){
	// 파일 읽기 준비
	std::ifstream in("test.txt", std::ios::binary);
	std::string s;

	int x;
	if(in.is_open()){
		in.read((char*)(&x),4);
		std::cout <<std::hex<<std::endl;
	} else{
		std::cout<<"파일을 찾을 수 없습니다." << std::endl;
	}

	return 0;
}
  • std::ifstream in("test.txt", std::ios::binary); : 이렇게 binary 값으로 데이터를 받을 수 있다
  • in.read((char*)(&x),4); : 4바이트 내용을 읽어라 라는 내용
    • char x[10]; in.read(x, 10); 이런식으로 배열을 지정해서 쉽게 코드를 짤 수 있음

파일 전체 읽기

파일 전체를 한번에 읽어보자

#include <fstream>
#include <iostream>
#include <string>

int main(){
	// 파일 읽기 준비
	std::ifstream in("test.txt");
	std::string s;

	if(in.is_open()){
		// 위치 지정자를 파일 끝으로 옮긴다.
		in.seekg(0,std::ios::end);
		
		// 그리고 그 위치를 읽는다. (파일의 크기)
		int size = in.tellg();

		// 그 크기의 문자열을 할당한다.
		s.resize(size);

		// 위치 지정자를 다시 파일 매 앞으로 옮긴다.
		in.seekg(0, std::ios::beg);

		// 파일 전체 내용을 읽어서 문자열에 저장
		in.read(&s[0], size);
		std::cout<<s<<std::endl;
	} else{
		std::cout<<"파일을 찾을 수 없습니다."<< std::endl;
	}
	return 0;
}

* 제가 참 좋아하는 노래 가사를 넣어봤어요

n.seekg(0, std::ios::end) : fseek 함수인데 g(get) 포인터 함수 전용 seek 입니다.

  • 파일 위치 지정자를 사용자 마음대로 위치를 바꿀 수 있음

int size = in.tellg() : ftell 함수인데 g(get) 포인터 함수 전용 tell 입니다.

  • 파일 위치 지정자의 위치(파일크기)를 반환

in.seekg(0, std::ios::beg) : 파일크기를 파악했으니 위치지정자를 원상복구

in.read(&s[0], size); : 파일 내용을 size만큼 읽어옵니다.

파일전체를 한줄 씩 읽어와보자

#include <fstream>
#include <iostream>
#include <string>

int main(){
	// 파일 읽기 준비
	std::ifstream in("test.txt");
	char buf[100];

	if(!in.is_open()){
		std::cout<<"파일을 찾을 수 없어용" << std::endl;
		return 0;
	}

	while(in){
		in.getline(buf,100);
		std::cout<<buf<<std::endl;
	}
	return 0;
}


잘된다.

in.getline(buf, 100) : 한번에 buf에 99자 까지 받을 수 있다.

  • 내가 원하는 문자까지 읽는 것도 같이 구현 가능하다.
    in.getline(buf, 100,'.') : ' . ' 마다 끊어서 가져올 수 있다.

while(in){ ... } : in에서 받아오는 데이터가 없거나 성공적이 않으면 false가 나오고, 평소에는 true 가 나온다.

getline 을 할때, 개행문자가 나오기전에 버퍼가 가득 찰 수 있다.
그러면 failbit를 키게 되는데, 정상적으로 값을 받아올 수 없게 된다.
이런 한계점 또한 보완방안이 있는데

#include <fstream>
#include <iostream>
#include <string>

int main(){
	// 파일 읽기 준비
	std::ifstream in("test.txt");

	if(!in.is_open()){
		std::cout<<"파일을 찾을 수 없어용" << std::endl;
		return 0;
	}

	std::string s;
	while(in){
		getline(in, s);
		std::cout<<s<<std::endl;
	}
	return 0;
}


역시 잘나온다.

getline(in, s); :
이 getline 함수는 ifstream에 정의되있지 않고, std::string 의 getline 이다.

  • 버퍼지정을 안하고 string으로 사용가능해서,
    기존의 ifstream 의 getline 보다 훨씬 편하다.

| Tip! : while 문 '조건'으로 in.eof 사용금지

  • eof 함수는 파일 위치 지시자가 파일 끝에 도달한 경우 true를 리턴한다.
  • eof 까지 못간 상태까지 가게되면 오류가 펑펑펑
  • 그냥 while(in) 을 하는게 좋음.

파일에 쓰기

#include <fstream>
#include <iostream>
#include <string>

int main(){
	std::ofstream out("write.txt");

	std::string s;
	if(out.is_open()){
		out << "Hello";
	}
	return 0;
}


저 파일이 없으면 저절로 생성된다.

덧붙이기도 가능하다.

#include <fstream>
#include <iostream>
#include <string>

int main(){
	std::ofstream out("write.txt",std::ios::app);

	std::string s;
	if(out.is_open()){
		out << "-Love never felt so good-";
	}
	return 0;
}

std::ofstream out("write.txt",std::ios::app);
이렇게 out 객체에 app을 주게되면, 파일에 스트림을 연결할때 지우지 새로안쓰고, 그대로 이어붙여 쓰게됨
저런 ios::binary, ios::app 외에 몇 개 더있음

  • ios::ate : 자동으로 파일끝에서 부터 읽기, 쓰기를 실시한다.(기존내용은 삭제해버림)
    • ios::app과 비슷해보이는데, 기존내용을 삭제하고 그 다음줄에 쓰는
      ate는 그닥 쓸일이 없다.
  • ios::trunc : 파일스트림을 열면 기존에 열던 내용들이 모두 지워짐.
    기본적으로 ofstream객체의 기본 설정임
  • ios::in ios::out : 파일에 입력 할 지 출력 할 지 지정가능하다.
    ifstream과 ostream 객체를 생성할 때 각각 이미 설정 됨

문자열 스트림(std::stringstream)

#include <iostream>
#include <sstream>

int main(){
	std::istringstream ss("123");
	int x;
	ss>>x;

	std::cout<<"입력받은 데이터 ::"<< x <<std::endl;

	return 0;
}


string으로 숫자입력을해서 받아왔다.

sstream 에는 std::istringstream 이 정의되었음. 문자열을 하나의 스트림이라고 생각하게 해주는 가상화 장치라고 보면 됨.
std::istringstream ss("123");

  • istringstream으로 "123" string 을 받았지만, 123 숫자로 입력받는 것과 동일한 효과를 낸다.
int x;
ss >> x;
  • 그래서 int 형 x에 ss가 들어갈 수 있었던 것.
#include <iostream>
#include <sstream>


double to_number(std::string s){
	std::istringstream ss(s);
	double x;

	ss>>x;
	return x;
}

int main(){
	std::cout<<"변환하자 :: 1.2 + 2 = "<< to_number("1.2") + to_number("2")<< std::endl;

	return 0;
}

  • atoi, atof 이런거 사용안해도 변환이 이렇게 쉽게 된다.

그럼 반대로 숫자를 string으로 바꿔보자

#include <iostream>
#include <sstream>
#include <string>


std::string to_str(double x){
	std::ostringstream ss(x);
	ss<<x;

	return ss.str();
}

int main(){
	std::cout<<"문자열로 변환 :: 1.2 + 2 = "<<to_str(1.2+2)<< std::endl;

	return 0;
}

  • 아이고 편해라..
std::ostringstream ss;
ss << x;
  • 여기서 변수를 거꾸로 문자열로 바꿔줌
return ss.str();
  • 그리고 str 함수로 현재 stream에 있는 값을 불러오기만 하면 끝.
profile
달리다 넘어져도 아픔마저 즐기려하는 사람

0개의 댓글