본문 바로가기

개발 공부/C++

따라하며 배우는 C++ 18. 입력과 출력

따라하며 배우는 C++ 18. 입력과 출력

 

18.1 istream으로 입력받기

 

 

#include <iostream>
#include <string>
#include <iomanip> //input / output manipulators

using namespace std;

int main()
{
	//기본적으로 stream은 버퍼에 저장되어 있는 걸
	//임시적으로 일부씩 꺼내오는 것
	
	char buf[10];
	cin >> setw(10) >> buf; //최대 10글자까지만 버퍼에서 받을 수 있도록 방지함
	//여기서 null 캐릭터 자리는 비워둬서 실질적으론 9글자까지만 가능함.

	cout << buf << endl;

	cin >> setw(10) >> buf;
	cout << buf << endl;

	cin >> setw(10) >> buf;
	cout << buf << endl;

	//아무것도 없을 때까지 버퍼에서 순차적으로 가져올 수 있다.
	return 0;

}

#include <iostream>
#include <string>
#include <iomanip> //input / output manipulators

using namespace std;

int main()
{
	char ch;
	while (cin >> ch)
		cout << ch;
	return 0;

}

위에서처럼 계속 입력받을 수 있다.

그러나 space가 사라진다.

원래는 빈 칸으로 입력자들의 구분을 해 줘서, 사라지는 것.

ex.

int i;
float f;
cin >> i >> f;
cout << i << " " << f << endl;

 

#include <iostream>
#include <string>
#include <iomanip> //input / output manipulators

using namespace std;

int main()
{
	char ch;
	while (cin.get(ch))
		cout << ch;
	return 0;

}

 

get으로 space도 받아올 수 있음.

이 때 char buf[5]; cin.get(buf,5);로 이용하면 최대 5글자씩 버퍼에서 받아옴.

 

cin.gcount(); //버퍼에서 몇 글자 읽어들였는지 숫자를 세어 주는 함수.

cin.getline(); //line 한 줄을 다 읽어들인다. (사이즈가 제한되어 있으면 그 사이즈만큼만 읽어오나,

               //한 줄을 다 읽어들이기 때문에 버퍼가 비게 될 수도 있다.

#include <iostream>
#include <string>
#include <iomanip> //input / output manipulators

using namespace std;

int main()
{
	char buf[100];
	cin.get(buf, 100);
	cout << cin.gcount() << " " << buf << endl;
	cin.getline(buf, 100);
	cout << cin.gcount() << " " << buf << endl;

}

getline은 줄바꿈 문자까지 읽어들인다.

 

#include <iostream>
#include <string>
#include <iomanip> //input / output manipulators

using namespace std;

int main()
{
	string buf;
	getline(cin, buf);
	cout << cin.gcount() << " " << buf << endl;
}

사이즈를 고려할 필요 없어서 편하다.

 

 

 

#include <iostream>
#include <string>
#include <iomanip> //input / output manipulators

using namespace std;

int main()
{
	char buf[1024];
	cin.ignore();
	cin >> buf;
	cout << buf << endl;
	//읽을 때 한 글자를 무시함.
	//인수가 생략되면 1글자, 넣으면 n글자.

	
	cin >> buf;
	//cout << (char)cin.peek() << endl;
	cout << buf << endl;
}

//peek은 한 글자를 들여닫보기만 하고, 실제로 꺼내진 않는다.

#include <iostream>
#include <string>
#include <iomanip> //input / output manipulators

using namespace std;

int main()
{
	char buf[1024];
	cin >> buf;
	cout << buf << endl;
	cin.unget();
	cin >> buf;
	cout << buf << endl;
}

unget은 마지막으로 가져온 글자를 버퍼에 도로 넣는다.

putback은 정해진 문자를 버퍼에 넣는다(cin.putback('A';)처럼)

 

 

18.2 ostream으로 출력하기

 

#include <iostream>
#include <string>
#include <iomanip> //input / output manipulators

using namespace std;

int main()
{
	cout.setf(std::ios::showpos);
	cout << 108 << endl;
	// +기호를 붙일 수 있다.

	cout.unsetf(std::ios::showpos);
	cout << 109 << endl;
	//지정했던 플래그를 해제한다.

	cout.unsetf(std::ios::dec);
	cout.setf(std::ios::hex); //10진수 플래그를 꺼주고 16진수 설정을 넣어야 함
	//cout.setf(std::ios::hex, std::ios::basefield);
	// cout<<std::hex; 도 가능.
	//이렇게 하면 한번에 가능.
	cout << 108 << endl;

	cout.setf(std::ios::uppercase); //대문자
	cout << 108 << endl;

	cout << std::dec; //다시 10진수로.
	cout << 108 << endl;

	cout << std::boolalpha;
	cout << true << " " << false << endl;

	cout << std::defaultfloat;
	cout << std::setprecision(3) << 123.456 << endl;
	cout << std::setprecision(4) << 123.456 << endl;
	cout << std::setprecision(5) << 123.456 << endl;

	cout << std::fixed;
	cout << std::setprecision(3) << 123.456 << endl;
	cout << std::setprecision(4) << 123.456 << endl;
	cout << std::setprecision(5) << 123.456 << endl;

	cout << std::scientific;
	cout << std::setprecision(3) << 123.456 << endl;
	cout << std::setprecision(4) << 123.456 << endl;
	cout << std::setprecision(5) << 123.456 << endl;

	cout << std::showpoint; //소수점 보이게
	cout << std::setprecision(3) << 123.456 << endl;
	cout << std::setprecision(4) << 123.456 << endl;
	cout << std::setprecision(5) << 123.456 << endl;

	cout << -12345 << endl;
	cout << std::setw(10) << -12345 << endl; //10글자 자리를 채워라
	cout << std::setw(10) << std::left << -12345 << endl;
	cout << std::setw(10) << std::right << -12345 << endl;
	cout << std::setw(10) << std::internal << -12345 << endl;

	cout.fill('*');
	cout << std::setw(10) << -12345 << endl; //10글자 자리를 채워라
	cout << std::setw(10) << std::left << -12345 << endl;
	cout << std::setw(10) << std::right << -12345 << endl;
	cout << std::setw(10) << std::internal << -12345 << endl;
}

 

 

 

18.3 문자열 스트림

 

지금까지 살펴본 입출력 stream은 console에 대한 작용을 했다면,

문자열 stream은 문자열 흐름의 버퍼 역할을 한다.

 

#include <iostream>
#include <sstream>
using namespace std;

int main()
{
	stringstream os;
	os << "Hello, World!"; //insertion operator
	os.str("Hello, World!");

	string str;
	os >> str;

	cout << str << endl; 	// >> : extraction operator

	string str2;
	str2 = os.str();
	cout << str2 << endl;
}

 

 

여기서 os.str()을 하면 이전의 내용은 사라지고 새 내용으로 덮어씌워진다.

os<<"Hello, World!"<<endl ; 이라고 하면 줄바꿈 문자도 스트림에 포함된다.

 

#include <iostream>
#include <sstream>
using namespace std;

int main()
{
	stringstream os;
	os << "12345 67.89";
	string str1;
	string str2;
	os >> str1 >> str2;
	cout << str1 << "|" << str2 << endl;

	int i = 12345;
	double d = 67.89;
	os << i << " " << d;
	os >> str1 >> str2;
	cout << str1 << "|" << str2 << endl;

}

space로 분리해서 집어넣을 수 있다.

#include <iostream>
#include <sstream>
using namespace std;

int main()
{
	stringstream os;
	os.str(""); //stream 초기화

	cout << os.str() << endl; //함수 오버로딩
	//이 경우 return함.

	//os.str(""); os.str(string());
	//버퍼를 지워줄 수 있음.
	//or.str(""); 은 state까지만 지워줌(??)

}

 

 

18.4 흐름 상태stream states와 입력 유효성 검증input validation

 

#include <iostream>
#include <cctype>
#include <string>
#include <bitset>

using namespace std;

void printCharacterClassification(const int& i)
{

}

void printStates(const std::ios& stream)
//filestream, i/ostream 모두 공통적으로 사용할 수 있다.
//C언어의 printf보다 stream(cout)이 더 길게 느껴질 수도 있으나
//C언어에선 fprint라는 별도의 파일 입출력 함수를 써야 하는 데 반해
//C++에선 stream을 이용해 파일 입출력에도 console 입출력에서 사용하던 그대로
//입출력을 행할 수 있다.
{
	cout << boolalpha;
	cout << "good() = " << stream.good() << endl; //상태가 좋은가
	cout << "eof()=" << stream.eof() << endl; //end of file인가
	cout << "fial()=" << stream.fail() << endl; //good의 반대.
	cout << "bad()=" << stream.bad() << endl; //상태가 좋지 않은가.
}

bool isAllDigit(const string& str)
{
	return true;
}

int main()
{
	while (true)
	{
		int i;
		cin >> i;
		printStates(cin);

		cout << boolalpha;
		//printStates처럼 flag자체를 false, true로 검증해 확인하는 방법이 있고
		//비트 마스크로 확인하는 방법이 있다.
		cout << std::bitset<8>(cin.rdstate()) << endl;
		cout << std::bitset<8>(std::istream::goodbit) << endl;
		cout << std::bitset<8>(std::istream::failbit) << endl;
		cout << !bool((cin.rdstate() & std::istream::failbit) != 0) << endl;
		//good bit가 모두 0이라서 추출이 되지 않으므로 failtbit로 돌려서 추출함
		//cin.rdstate() == stad::istream::goodbit; 로 해도 됨

		//printCharacterClassfication(i);

		cin.clear();
		cin.ignore(1024, '\n');

	}

}

정수 넣을 자리에 문자를 넣으면 문제가 발생하고,

실수를 넣으면 절삭될 수 있다.

 

 

#include <iostream>
#include <cctype>
#include <string>
#include <bitset>

using namespace std;

void printCharacterClassification(const int& i)
{
	//cctype에 들어있는 함수들
	cout << boolalpha;
	//여기선 bool이 아닌 integer로 return함.
	//
	cout << "isalnum " << bool(std::isalnum(i)) << endl;
	cout << "isblank " << bool(std::isblank(i)) << endl;
	cout << "isdigit " << bool(std::isdigit(i)) << endl;
	cout << "islower " << bool(std::islower(i)) << endl;
	cout << "Isupper " << bool(std::isupper(i)) << endl;
}

void printStates(const std::ios& stream)
//filestream, i/ostream 모두 공통적으로 사용할 수 있다.
//C언어의 printf보다 stream(cout)이 더 길게 느껴질 수도 있으나
//C언어에선 fprint라는 별도의 파일 입출력 함수를 써야 하는 데 반해
//C++에선 stream을 이용해 파일 입출력에도 console 입출력에서 사용하던 그대로
//입출력을 행할 수 있다.
{
	cout << boolalpha;
	cout << "good() = " << stream.good() << endl; //상태가 좋은가
	cout << "eof()=" << stream.eof() << endl; //end of file인가
	cout << "fial()=" << stream.fail() << endl; //good의 반대.
	cout << "bad()=" << stream.bad() << endl; //상태가 좋지 않은가.
}

bool isAllDigit(const string& str)
{
	return true;
}

int main()
{
	while (true)
	{
		char i;
		cin >> i;
		printStates(cin);
		printCharacterClassification(i);

		cin.clear();
		cin.ignore(1024, '\n');

	}

}

 

#include <iostream>
#include <cctype>
#include <string>
#include <bitset>

using namespace std;

bool isAllDigit(const string& str)
{
	bool ok_flag = true;
	for(auto e:str)
		if (!std::isdigit(e))
		{
			ok_flag = false;
			break;
		}
	return ok_flag;
}

int main()
{
	cout << boolalpha;
	cout << isAllDigit("1234") << endl;
	cout << isAllDigit("a1234") << endl;

}

 

isblack는 위처럼 문자열 안에서 한 문자씩 비교할 때 쓰인다!

 

 

 

18.5 정규 표현식regular expressions 소개

 

 

#include <iostream>
#include <regex> //c++ 11부터 들어옴.

using namespace std;


int main()
{
	regex re("\\d"); //\d는 숫자가 하나인지 아닌지 판별함.
			//앞에 있는 \는 \를 문자로 쓰기 위한 것
			//d+이면 한 개 이상의 숫자
			//d*이면 0개 이상의 숫자
	//regex re("[[:digit:]]{3}");
	//digit을 3개를 딱 맞춰 받겠다. \d와 [[:digit:]]과 같다.
	//regex re("[A-Z]+");
	//대문자 A~Z까지의 문자를 한 개 이상 받겠다.
	//{1, 5}를 하면 최소 1개, 5개 이하.
	//regex re("[A-Z]{3}");
	//regex re("([0-9]{1})([-]?)([0-9]{1,4})");
	//0~9까지 숫자 중 하나, 뒤에는 -가 있어도 되고, 없어도 되고.
	string str; 
	while (true)
	{
		getline(cin, str);
		if (std::regex_match(str, re)) //정규표현식과 맞는지 비교함
			cout << "Match" << endl;
		else
			cout << "No match" << endl;
		//print matches,, 매치되는 부분만 프린트.
		{
			auto begin = std::sregex_iterator(str.begin(), str.end(), re);
			auto end = std::sregex_iterator();
			for (auto itr = begin; itr != end; ++itr)
			{
				std::smatch match = *itr;
				cout << match.str() << " ";
			}
			cout << endl;
		}
		cout << endl;
	}

}

 

 

18.6 기본적인 파일 입출력

 

#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib> //exit()
#include <sstream>
using namespace std;


int main()
{
	//writing
	if (true)
	{
		ofstream ofs("my_fisrt_file.dat"); //ios::app, ios::binary
		//ofs.open("my_first_file.dat");

		if (!ofs)
		{
			cerr << "Couldn't open file " << endl;
			exit(1);
		}

		ofs << "Line 1" << endl;
		ofs << "Line 2" << endl;
		//ofs는 아스키 코드로 출력을 한다. 메모장으로 열기 가능.

		/* 
		const unsigned num_data = 10;
		ofs.write((char*) &

		for (int i = 0; i < num_data; ++i)
			ofs.write((char*)&i, sizeof(i));

		ss << "Line 1" << endl;
		ss << "Line 2" << endl;
		string str = ss.str();

		unsigned str_length = str.size();
		ofs.write((char*)&str_length, sizeof(str_length));
		ofs.write(str.c_str(), str_length);

		ofs.close();
		*/
	}

	//reading
	if (true)
	{
		ifstream ifs("my_first_file.dat");
		if (!ifs)
		{
			cerr << "Cannot open file" << endl;
			exit(1);
		}

		while (ifs)
		{
			std::string str;
			getline(ifs, str);
			std::cout << str << endl;
		}
	}

}

 

 

#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib> //exit()
#include <sstream>
using namespace std;


int main()
{
	//writing
	if (true)
	{
		ofstream ofs("my_fisrt_file.dat"); //ios::app, ios::binary
		//ofs.open("my_first_file.dat");

		if (!ofs)
		{
			cerr << "Couldn't open file " << endl;
			exit(1);
		}

		
		const unsigned num_data = 10;
		ofs.write((char*)&num_data, sizeof(num_data));

		for (int i = 0; i < num_data; ++i)
			ofs.write((char*)&i, sizeof(i));
		//위는 바이너리로 저장하는 방법
		//아스키 코드로 저장하면 프로그램이 느려진다.

		/*
		ss << "Line 1" << endl;
		ss << "Line 2" << endl;
		string str = ss.str();

		unsigned str_length = str.size();
		ofs.write((char*)&str_length, sizeof(str_length));
		ofs.write(str.c_str(), str_length);
		*/
		//ofs.close(); //not necessary
		
	}

	//reading
	if (true)
	{
		ifstream ifs("my_first_file.dat");
		if (!ifs)
		{
			cerr << "Cannot open file" << endl;
			exit(1);
		}

		unsigned num_data = 0;
		ifs.read((char*)&num_data, sizeof(num_data));
		//바이너리 파일은 꼭 파일의 크기를 먼저 인지해야 한다.
		for (unsigned i = 0; i < num_data; ++i)
		{
			int num;
			ifs.read((char*)&num, sizeof(num));
			cout << num << endl;
		}
	}

}

 

 

if (true)
	{
		ofstream ofs("my_first_file.dat", ios::app); 
		if (!ofs)
		{
			cerr << "Couldn't open file" << endl;
			exit(1);
		}

		ofs << "Line 1" << endl;
		ofs << "Line 2" << endl;
		
	}

 

파일쓰기를 append모드(app)로 하면 이미 있는 파일을 초기화해서 쓰지 않고,

뒤에 덧붙여 쓴다.

 

//출력할 데이터를 문자열 하나에 쫙 담는 경우도 있다.
ss << "Line 1" <<endl;
ss << "Line 2" << endl;
string str = ss.str();

unsigned str_length = str.size();
ofs.write((char*)&str_length, sizeof(str_length));
ofs.write(str.c_str(), str_length); 
//파일 쓰기


//파일 읽기
unsigned str_len = 0;
ifs.read((char*)&str_len, sizeof(str_len));

string str;
str.resize(str_len);
ifs.read(&str[0], str_len);

cout<<str<<endl;

 

 

 

 

 

18.7 파일의 임의 위치 접근하기

 

#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib> //exit()
#include <sstream>
using namespace std;


int main()
{
	const string filename = "my_file.txt";
	//make a file
	{
		ofstream ofs(filename);
		for (char i = 'a'; i <= 'z'; ++i)
			ofs << i;
		ofs << endl;
	}

	//read the file
	{
		ifstream ifs("my_file.txt");
		ifs.seekg(5); //ifs.seekg(5, ios::beg);
		cout << (char)ifs.get() << endl;

	}

}

 

여기서 ifs.seekg(5)는 파일에서 다섯번째 글자를 읽는다.

다시 ifs.seekg(5, ios::cur)를 호출하면 현재 다섯번째 읽은 글자에서 다시 다섯 글자를 가서 그 문자를 읽어온다.

 

//ifs.seekg(0, ios::end);

파일 끝으로부터 맨 마지막 문자.

//ifs.tellg()

현재 위치를 알려줌.

 

ifs.seekg(-5, ios::end);

//파일 끝으로부터 5글자 앞.

 

string str;

 

getline(ifs, str);

//한 글자를 쫙 읽어옴

 

 

//fstream iofs(filename, ios::in | ios::out);
fstream iofs(filename);
iofs.seekg(5);
cout<< (char)iofs.get() << endl; //read
iofs.seekg(5);
iofs.put('A'); //write

 

위 예제는 파일 읽고, 쓰고를 한번에 하는 것.

seekg로 읽은 후 put을 하면 그 자리에 해당 문자가 덮어 씌워진다.