본문 바로가기
개발 (언어)/C++

[Modern C++] Unicode, MBCS, WBCS

by 진현개발일기 2024. 3. 29.

■ ASCII

char는 C++에서 1바이트를 차지한다. unsinged 라면 0~255 사이의 숫자만 표현할 수 있다.
컴퓨터 출시 초기에 작업을 할 때는 미국에서 군사용 목적으로 사용되었고 이때 ASCII코드를 사용했었다. 그러므로 이는 영어만 고려되어있는 체계인 것이다.

아스키 코드는 0~127사이의 숫자만 사용하고 있고 이는 7bit인 것을 알 수 있다. 

[출력 예시]

const char* test = "aa유진현";
cout << test << endl;

(★) char은 기본적으로 1바이트이지만 위와 같이 한국어 등이 섞여있으면 자동으로 2바이트로 늘어난다.

 

■ ANSI

ASCII + 각국 언어별로 128개를 기호로 나타낸다.

하지만, 중국어나 한국어만 봐도 128개로 모든 문자를 표현할 수 없음. 그래서 아래와 같은 방법을 활용한다.

 

[표현 방법]

127번까지는 ASCII기반이긴 하지만, 128번 부터는 새로운 페이지 번호를 정하게 된다.
한국어는 대표적으로 CP949라는 포맷을 이용하고 이 환경에 따라서 모든 문자들에 번호가 매겨진다.

이를 확인해 봤다.

 

[한계점]

만약에 중국어로 디폴트 되어있다면 cp949가 아닌 다른 페이지로 설정될 것이다.

 

이렇게 나라마다 다른 페이지로 설정되었을 때 문제가 되는 건 한국어는 2Byte씩 차지하고 있고 "유진현"을 16진수로 변경한다면 "c0 af c1 f8 c7 f6"이고 한국어에서 c0이 '유'이지만, ANSI의 특성상 환경마다 문자가 다르다는 점 때문에, 중국에서 통신하고 있는 사람이 c0을 받으면 우리가 의도한 '유'가 아니라 c0에 해당되는 중국어 문자 혹은 깨진 문자를 받게 될 것이다.

메모리 확인 1
메모리 확인 2

 

■ Unicode

위 ANSI 코드의 한계점을 보완하기 위해 Unicode가 출현했다. 

[원리]

'동일 번호 = 동일 문자 = 동일한 유니코드'

만약 유니코드로 설정하고 유니 코드 환경 내에서 c0 af이 '유'라면 모든 언어 환경에서 c0 af이 '유'로 출력되는 것이다.

 

[인코딩]

대표적으로 UTF-8, UTF-16이 있다. 간단하게 특징을 비교하자면 아래와 같다.

UTF-8 UTF-16
· 영어(1Byte), 한국어 및 중국어 같은 문자(3Bytes)
· 가변 길이 인코딩 (MBCS)
· 대부분의 언어(2Bytes), 특수문자(4bytes)
· 고정 길이 인코딩 (WBCS)

 

[UTF-8 출력]

아래와 같이 코드를 짜봤다. 

이를 그냥 출력하면 아래와 같이 이상한 문자로 변환이 되는데 UTF-8을 출력하기 위해선 규칙을 몇 가지 따라야 한다.

* UTF-8 규칙

(1) 문자 앞에 u8을 붙인다.

(2) setlocale(LC_ALL, "en_US.UTF-8"); 를 실행해준다.

 

이 규칙에 맞게 수정을 해준 뒤 다시 출력해보면 정상 출력이 되는 것을 볼 수 있다.

■ MBCS, WBCS

(1) MBCS (멀티바이트 집합; Multi Byte Character Set)

- 가변  길이 인코딩 (ex. ANSI, UTF-8)

(Ansi같은 경우 char를 사용할 시 한국어 등 외국어가 들어가면 2바이트로 변경된다는 점을 기억하면 된다.)

 

(2) WBCS (유니코드 집합; Multi Byte Character Set)

- 고정 길이 인코딩 (ex. UTF-16)

- 위에서 UTF-8, UTF-16을 공부할 때 사용한 유니코드랑 WBCS의 유니코드랑은 다른 용어다.

 

[WBCS 출력 방법 (★)]

1. UTF-16에서는 setlocale함수를 사용하여 로케일을 설정하는 방법이 적합하지 않다. 대신에 UTF-16은 주로 windows에서 사용된다.

 

2. 그러므로 windows 환경에서는 일반적으로 Unicode를 다루기 위한 라이브러리나 윈도우 API를 사용한다. 그 윈도우 API를 사용하는 방법은 'wchar_t', 'wstring' 과 같은 타입을 이용하고 출력할 때 'wcout'을 cout 대신 사용하면 된다.

 

3. 또한 문자열 앞에 대문자 L 혹은 소문자 u를 붙여 사용한다.

 

■ 결론

결국에는 어떤 걸로 문자를 관리할지 결정해야 한다. 그냥 string을 사용해서 문제가 될 건 아니지만 사용한다면 문제가 생길 수 있다.

 

요즘엔 WBCS를 사용하는 것이 합리적이고 그래서 보통 언리얼이나 유니티같은 상용 엔진은 기본적으로 2바이트 짜리인 UTF-16으로 되어있고 그로 인해 서버랑 클라가 통신할 때 비교적 편안한 감이 없지않아 있다.

그래서 C++에서 작성할 때
wchar_t
wstring
wcout 
이런 식으로 사용하는 것을 습관들이자.

(!) 하지만 가끔가다 옛날 함수를 이용하다보면 const char*와 같이 멀티바이트 집합으로 반환해주는 경우가 있으므로 왔다갔다 해야할 때가 있다.

그걸 위해 헬퍼클래스 같은 것을 만들어주고 동적으로 적용할 수 있도록 해주자.

[만약 언리얼에서 UTF-8를 사용한다면 언리얼 쪽에 보낸 다음 그걸 UTF-16으로 변환해야하는 작업이 필요하다]

 

(*) 추가로 글쓴이 본인도 실무에서 날짜 관련 데이터를 저장하기 위해 클라우드와 통신할 때 해당 지식이 없어 문자열을 그냥 보내다가 베트남 유저들이 아예 로딩에서 게임 진행이 안되는 버그를 겪어봤었다.

728x90

'개발 (언어) > C++' 카테고리의 다른 글

[Modern C++] shared_ptr  (0) 2024.03.31
[Modern C++] rvalue-ref (오른값 참조)  (0) 2024.03.31
[Modern C++] String  (0) 2024.03.29
[C++] 동적 미로 생성, 우수법 탈출 AI  (0) 2024.03.24
[C++, 백준] 알파벳 찾기  (0) 2022.10.10