Post

C++: ANSI 코드를 이용한 문자 효과

C++: ANSI 코드를 이용한 문자 효과

ANSI Escape code(ANSI escape sequences)

ASCII로도 유명한 미국 국립 표준 협회(American National Standards Institute)에서 만든 규격으로, 데이터의 전송과 컨트롤 시그널의 전송이 하나의 채널에서 이루어지는 환경, 인밴드 시그널링(In-band Signaling) 제어를 위해 만들어놓은 규격이다. 본디 통신, 비디오의 송출과 전송에 관련된 규격이지만, PC업계에 소개된 이후 업체마다 각자의 규격으로 파편화되어있던 초기 PC 보급시기에, 기능들의 규격을 통일시키는데 ASCII와 함께 기여했다. 오래전부터 리눅스와 유닉스쪽에서 지원되었고, 윈도우가 오히려 뒤늦게 2016년 Windows10의 버전 업데이트를 진행하면서 지원하게 되었다.(다만 터미널이나 개발환경에 따라 일부 기능은 지원하지 않을 수도 있다.)

문자의 크기, 이텔릭이나 밑줄같은 효과, 깜빡임, 커서의 위치 제어등 콘솔환경에서의 출력을 제어하는 기능들을 제공한다. 이는 딱딱하고 가독성이 부족할 수 있는 기본 콘솔 환경에서 제공할 수 있던 몇 안되는 화려함에 관한 기능이였고, 이는 문법 하이라이트, 프로그램의 출력 결과 생성등에 두루 사용되었다. “\033”으로 시작해서 “m”으로 끝나는데, 그 사이에 들어간 문자에 따라 이후 나올 문자들에 효과를 부여한다. 이는 내가 \033[0m으로 초기화해주지 않는다면 그 콘솔창이 끝날 때까지 효과를 이어간다는 말이다. 모두 다른 효과로 분리해서 합칠 수도 있지만, ‘;’를 이용해 하나의 단어로 뭉쳐놓을 수도 있다. 256색 사용을 위해 여러 ANSI값을 한번에 놓을 때가 대표적이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <iostream>
#include <string>
#include <sstream>
using namespace std;

int main() {
	stringstream SS;
	//기본 ANSI 출력 글자효과, 배경색 설정, 글자색 설정등이 존재 (\033[0m - \033[107m)
	for (int i = 0; i < 110; i++) {
		SS.str("");
		//적용한 색상으로 숫자 스트림에 입력후 \033[0m으로 초기화
		SS << "  \033[" + to_string(i) + "m" << i << "\033[0m";
		SS << string((15 - SS.str().size()) / 2, ' '); //공백 추가
		cout << SS.str();
		if (i % 20 == 0) cout << endl;
	}
	cout << "\n\n";

	//8비트 색상 글씨 출력, 256색(\033[38;5;0m -  \033[38;5;255m)
	for (int i = 0; i < 257; i++) {
		SS.str("");
		//적용한 색상으로 숫자 스트림에 입력후 \033[0m으로 초기화
		// \033[:시작문자
		// 38: 전경(foreground)색상 선택
		// 5: 256색 모드, 2: (R,G,B)모드, 256^3색
		SS << "  \033[38;5;" + to_string(i) + "m" << i << "\033[0m";
		SS << string((20 - SS.str().size()) / 2, ' '); //공백 추가
		cout << SS.str();
		if (i % 20 == 0) cout << endl;
	}
	cout << "\n\n";

	//8비트 색상 배경 출력, 256색(\033[38;5;0m -  \033[38;5;255m)
	for (int i = 0; i < 260; i++) {
		SS.str("");
		//적용한 색상으로 숫자 스트림에 입력후 \033[0m으로 초기화
		// \033[:시작문자
		// 48: 배경색 선택 모드
		// 5: 256색 모드
		SS << "  \033[48;5;" + to_string(i) + "m" << i << "\033[0m";
		SS << string((20 - SS.str().size()) / 2, ' ');	//공백 추가
		cout << SS.str();
		if (i % 20 == 0) cout << endl;
	}
}

출력은 다음처럼 코드별 차이를 보여줄 것이다. image

유의점: String 내장함수 호환성

위의 코드에서 공백을 넣는 코드를 보았다면 뭔가 이상함을 느낌 사람도 있겠다. 출력할 문자는 몇개의 숫자뿐인데 공백 갯수를 20에서 빼며 계산할 이유가 있을까? 5정도에서만 빼도 충분한 것 아닐까? ANSI 이스케이프 코드를 이루는 부분들도 컴퓨터는 문자로 인식한다. 다만 터미널 창에 출력을 하지 않을 뿐이다. 때문에 기존의 스트링 접근/수정 방법처럼 인덱스를 기반으로 하는 함수에 접근한다면, 효과의 시작부분 혹은 효과의 초기화 부분이 망가져 원치 않는 효과가 나타날 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include <string>
using namespace std;

int main() {
	//붉은색 abc 출력
	string A = "\033[91m abc \033[0m";
	cout << A << endl;

	//앞 4개 문자 제거(\0, 3, 3, [), 색 없는 1m abc 출력
	cout << A.substr(3) << endl;

	//후방 초기화 코드 손상, abc t 출력 후 프로그램 종료 메세지까지 붉은색 출력
	cout << A.replace(11,5, "tt") << endl;
}

UI는 요즘은 HTML, CSS나 다른 GUI 라이브러리를 사용하는경우도 많겠으나, C++로 콘솔 프로그램을 만들어야 한다면 고려해야할 내용일 수 있겠다.

This post is licensed under CC BY 4.0 by the author.