Coding Memo

[C++] 여러가지 출력 방법 (스트림 서식화) 본문

Language/C++

[C++] 여러가지 출력 방법 (스트림 서식화)

minttea25 2023. 7. 11. 22:48

C++에서

출력을 표처럼 간격에 맞춰서 깔끔하게 출력하고 싶다.

double이나 float 타입의 숫자를 특정 자릿수까지 출력하고 싶다.

특정 숫자를 16진수, 8진수로 바로 출력하고 싶다.

double을 출력했는데 자릿수가 얼마 안나온다.

...

int main()
{
	double pi = 3.1415926535;
	cout << pi << endl;

	return 0;
}

// output
// 3.14159

어떻게 해야 할까?

 

C++에는 이를 위해 다양한 서식화(formatting) 기능이 있다.

 

입출력 스트림(키보드, 파일 등)에는 입출력 조작자(I/O Manipulator)가 사용된다. 이 입출력 조작자로 출력 형식을 지정할 수 있다.

 

입출력 조작자에는 어떤 것이 있는지 알아보고 테스트 해보자.

 

참고: iomanip 헤더를 미리 추가하자.

#include <iomanip>

precision

 

정밀도를 설정하여 유효숫자를 더 많이 출력하게 해준다.

예를 들어 precision을 10으로 설정하였다면, 출력되는 숫자를 10자리로 하여 출력한다. 

 

precision 사용 시 주의해야 할 점은 2가지이다.

1. precision을 한번 설정하면 다시 값을 넣어주기 전까지 그 값이 계속 유지된다.

2. precision은 전체 자릿수에 대한 값으로, 정수부와 소수부를 합친 자릿수가 그 값이 된다.

#define _USE_MATH_DEFINES

#include <iostream>
#include <cmath>
#include <iomanip>

using namespace std;

int main()
{
	double pi = M_PI;
	double pi2 = 3.1415926535;
	float f = 12.345f;

	cout << "Before precision" << endl;
	cout << "pi: " << pi << endl;
	cout << "pi2: " << pi2 << endl;
	cout << "f: " << f << endl;

	cout << '\n';

	cout.precision(10);
	cout << "After cout.precision(10)" << endl;
	cout << "pi: " << pi << endl;
	cout << "pi2: " << pi2 << endl;
	cout << "f: " << f << endl;

	return 0;
}

/* output
Before precision
pi: 3.14159
pi2: 3.14159
f: 12.345

After cout.precision(10)
pi: 3.141592654
pi2: 3.141592654
f: 12.34500027
*/

참고: f의 값은 12.345인데 왜 12.34500027로 출력이 되었을까 생각해보자.

 

물론, 소수점 아래 숫자의 길이만 설정하여 출력도 가능하다. 

precision를 설정해주기 전에 "cout << fixed"를 넣어주면 된다.

"cout << fixed" 이후에 precision에 값을 넣으면 그 값만큼 소수점 아래 숫자가 출력이 된다.

int main()
{
	double pi = M_PI;
	double pi2 = 3.1415926535;

	cout << fixed; // = cout.setf(ios_base::fixed);
	cout.precision(5);

	cout << pi << endl;
	cout << pi2 << endl;

	return 0;
}
/* output
3.14159
3.14159
*/

cout.precision()으로 값을 설정할 수도 있지만 cout << setprecision()으로도 값을 넣어 줄 수 있다.

(마찬가지로 계속 설정한 값이 유지된다.)

int main()
{
	double pi = M_PI;
	double pi2 = 3.1415926535;

	cout << "setprecision(20) pi: " <<  setprecision(20) << pi << endl;
	cout << "setprecision(10) pi2: " << setprecision(10) << pi2 << endl;

	return 0;
}
/* output
setprecision(20) pi: 3.141592653589793116
setprecision(10) pi2: 3.141592654
*/

width

 

출력의 너비를 설정할 수 있다. (숫자 뿐만 아니라 출력하는 모든 것에 영향을 미친다.)

표를 출력할 때나, 결과값을 한쪽에 정렬하여 출력하고 싶을 때 유용하다.

 

width에 관해서도 주의해야 할 점이 두가지 있다.

1. width값이 변하면 바로 다음 출력의 너비만 바꾼다. (이후 출력은 width와 관계가 없다.)

2. 지정한 너비는 출력의 최소너비란 의미로, 출력이 이 길이를 넘어가게 될 수 있다. (출력 너비를 강제하는 것이 아니다.)

즉, 여러개를 정렬하여 출력하고 싶으면, 출력할 때 마다 설정을 해주어야한다.

int main()
{
	double pi = M_PI;
	double pi2 = 3.1415926535;

	cout.width(5);
	cout << "pi: ";
	cout.width(20);
	cout << pi << endl;

	cout.width(5);
	cout << "pi2: ";
	cout.width(20); 
	cout << pi2 << endl;

	return 0;
}
/* output
 pi:              3.14159
pi2:              3.14159
*/

fill

 

설정된 width에 맞게 출력하고 남은 공간을 설정한 값으로 채울 수 있다.

마찬가지로 fill 또한 한번 설정하면 precision과 동일하게 계속 유지된다.

int main()
{
	double pi = M_PI;
	double d = 1.23456789;

	cout.fill('-');

	cout.width(5);
	cout << "pi: ";
	cout.width(20);
	cout << pi << endl;

	cout.width(5);
	cout << "d: ";
	cout.width(20);
	cout << d << endl;

	return 0;
}
/* output
-pi: -------------3.14159
--d: -------------1.23457
*/

left, right

 

width 설정 후 값을 어느 쪽을 기준으로 정렬할 건지 설정 할 수 있다.

마찬가지로 플레그 자체를 변경하는 것이기 때문에 계속 옵션이 유지된다.

setf로 right나 left를 직접 넣어 설정하거나 cout << right 나 cout << left와 같이 사용하여도 된다.

int main()
{
	double pi = M_PI;
	double d = 1.23456789;

	cout.setf(ios_base::left); // cout << left
	cout.fill('-');

	cout.width(5);
	cout << "pi: ";
	cout.width(20);
	cout << pi << endl;

	cout.width(5);
	cout << "d: ";
	cout.width(20);
	cout << d << endl;

	return 0;
}

/* output
pi: -3.14159-------------
d: --1.23457-------------
*/

8진수, 10진수, 16진수

 

숫자를 별다른 변환 없이 8진수, 10진수, 16진수로 바로 출력할 수 있다.

int main()
{
	int a = 1000;
	cout << "octal: " << oct << a << endl;
	cout << "decimal: " << dec << a << endl;
	cout << "hex: " << hex << a << endl;

	return 0;
}

/* output
octal: 1750
decimal: 1000
hex: 3e8
*/

bool

 

우리 C++는 다른 언어와 달리 bool 값을 출력할 때 0 (false) 또는 1 (true) 값으로 표시해 준다. true/false가 아니라 숫자로...

다른 언어 처럼 true/false를 출력하기 위해서는 cout.setf(ios_base::boolalpha)를 해주거나 cout << boolalpha를 넣어주면 된다.

int main()
{
	bool a = 1 < 3;
	bool b = 1 > 3;

	cout << a << endl;
	cout << b << endl;

	cout.setf(ios_base::boolalpha);
	cout << "After set boolalpha" << endl;
	cout << a << endl;
	cout << b << endl;

	return 0;
}

/* output
1
0
After set boolalpha
true
false
*/

서식화

 

이미 눈치챈분들도 있겠지만, 서식화 옵션은 flag로 되어 있으며, cout.setf()함수로 플레그를 지정할 수 있다.

(반대로 cout.unsetf() 함수로 플레그를 해제할 수 있다.)

플레그가 설정된 값은 해제 전까지 계속 유효하다. (boolalpha, fixed 등)

 

각 서식화 옵션들은 비트로 표현되는 플레그 값을 가지고 있다.

따라서 OR 연산을 통해 여러 개의 옵션을 동시에 활성화 하거나 비활성화 시킬 수 있다.

 

여전히 width 설정은 매번 해주어야 한다는 것 잊지말자.

 

#define _USE_MATH_DEFINES

#include <iostream>
#include <cmath>
#include <iomanip>

using namespace std;

int main()
{
	double pi = M_PI;
	double d = 1.23456789;
	bool a = 1 < 3;

	auto old_precision = cout.precision(); // std::streamsize = long long
	auto old_fill = cout.fill();
	cout.setf(ios_base::boolalpha | ios_base::fixed | ios_base::left);
	cout << setfill('_');
	cout.precision(10);
	cout << setw(5) << "pi: " << setw(20) << pi << endl;
	cout << setw(5) << "d: " << setw(20) << d << endl;
	cout << setw(5) << "a: " << setw(20) << a << endl;

	cout << "\nClean all manipulators" << endl;
	cout.unsetf(ios_base::boolalpha | ios_base::fixed | ios_base::left);
	cout.precision(old_precision);
	cout.fill(old_fill);
	cout << setw(5) << "pi: " << setw(20) << pi << endl;
	cout << setw(5) << "d: " << setw(20) << d << endl;
	cout << setw(5) << "a: " << setw(20) << a << endl;

	return 0;
}

/* output
pi: _3.1415926536________
d: __1.2345678900________
a: __true________________

Clean all manipulators
 pi:              3.14159
  d:              1.23457
  a:                    1
*/

 


위에 소개한 입출력 조작자 외에도 더 많은 조작자들이 있다. (showpos, scientific, basefield 등등...)

 

?: dec, hex, oct는 flag에 넣어도 적용이 안된다... 이유가 뭘까...