멀티바이트와 유니코드

200원짜리개발자·2023년 8월 27일
0

C++

목록 보기
36/39
post-thumbnail

우리는 저번시간에 string이라는 것을 공부했고 stirng은 char의 집합이라고 볼 수 있다.

우리가 문자열을 출력할 때

const char* test = "twotowow";
cout << test << endl;

이런식으로 출력을 할 수 있다.

하지만 이것을 한국어로 하게 된다면 어떻게 될까? (영어만 사용할 것이라면 상관이 없지만 한국어, 일본어, 중국어 등등.. 다른나라에 서비스를 제공해야한다면..)

const char* test = "이백원";
cout << test << endl;

이런식으로 하면 과연 문자가 깨질까? 제대로 출력이 될까?
해보면 출력은 된다.

char는 1바이트이다. 1바이트는 -128 ~ 127(unsigned면 0 ~ 255)까지 나타낼 수 있다.

ASCII

옛날에 컴퓨터를 이용해서 작업을 할 때 ascii코드를 사용하여 작업을 하였다. (미국에서 만듬)

그래서 ascii코드는 0 ~ 127까지 사용을 하고 있다. 7비트를 사용하고 있고 0 ~ 128만 남아있는데 어떻게 한국어까지 표현을 할 수 있다는게 대단하다고 볼 수 있다.

ANSI

계속 흐름을 따라가본다면,
이제 세상에 컴퓨터가 보급이 되면서 외국어도 표시하게 되었다. 그때는 인터넷이 활발하지 않았고 통신을 고려하지 않았기에 사람들이 (ANSI)ascii + 각국 언어별로 기호를 만들었다. 하지만 중국어나 한국어같은 경우는 남은 걸로 만들 수는 없다.

그래서 ascii기반으로 127번까지는 ascii이지만 그 번호를 넘어가게 된다면 새로운 페이지번호를 정하게 된다. 대표적으로 한국어는 CP949라는 포멧이 존재한다.

ANSI 사용한다고 하면 우리의 환경에 따라서 번호가 매겨진다.

이걸 확인하고 싶다면

setlocate(LC_ALL, ""); // 기본 세팅으로 만들기
cout << "LC_ALL: " << setlocate(LC_ALL, NULL) << endl;

이런식 작성하여 실행을 해본다면 확인할 수 있다.
우리 한국 Windows환경에서는 위에서 말한 cp949일 것이다.
하지만 우리가 로컬 언어설정을 다른언어로 하였다면 다르게 나올 것이다.

const char* test = "aaa이백원";

test라는 문자열이 있을 때 메모리에 test에 접근해보면 aaa는 616161 1바이트씩 차지를 하고 있지만 한국어 같은 경우에는 2바이트씩 차지하고 있다.
하지만 이건 문제가 되지 않는다. 진짜 문제가 되는 것은 다른 언어의 사람과 통신할 때이다.

만약 "이"가 b5 a1이라고 하자 그런데 ANSI라는 기법에서는 환경마다 문자가 다 다르다..
만약 내가 중국친구에서 "이"이라는 것을 보내주려고 한다면 중국친구 입장에서는 이라는 것이 자기 로컬환경에서보면 아예 다른 문자(깨진 문자)로 보이게 된다.
그래서 통신이 불가한다. (영어로만 할 수 있다)

결국 각국 로컬 환경에 따라 달라지기에 통신, 온라인게임..으로 가면 난리가 나는 것이다.

그래서 ASCII는 영어만 고려한다는 단점이 있고
ANSI는 동일한 키코드가 동일한 문자가 아니라는 단점이 있어서 결국 우리는 유니코드까지 가게 되는 것이다.

유니코드

그래서 유니코드는 문제를 해결하기 위해서 동일 번호 = 동일한 문자 = 동일한 유니코드라고 정의를 하였다.

그래서 앞으로 유니코드를 사용하게 되면 b5 a1이라는 숫자는 무조건 한국어 "이"라고 고정값이 된다. 하지만 유니코드는 동일 번호는 동일한 문자라는 것을 말하는 것이지
유니코드가 한개만 있는 것이 아니다.

여기서 인코딩이라 해서 여러가지 방식(규칙)이 있다.
대표적으로 UTF-8, UTF-16등이 있다.

그럼 둘이 어떤 차이가 있을까?

UTF-8 : 영어(1), 한국어/중국어(3) // 영어만 사용한다면 이점이 있음
UTF-16 : 영어(2), 한국(2), 중국어(2) // 전체적으로 서비스할 것이라면 이점이 있음

어떤식으로 동작할까?
우리가 기본적으로 설정한 CP949방식 (한국어 특수화)
const char* test = "aaa이백원";

UTF-8방식

auto tewst2 = u8"aaa이백원입니다";
cout << test2 << endl;

하지만 바로 출력하면 깨져서 나오기때문에

auto test2 = u8"aaa이백원입니다";
setlocate(LC_ALL, "en_US.UTF-8");
cout << test2 << endl;

이런식으로 바꿔주고 출력해줘야 한다.

그래서 규칙(방식)이 무엇인지를 잘 보아야 한다.

그리고 결국에는 char형이 1바이트로 표현하는 것은 맞지만,
const char*는 char형(1바이트)라고 주장을 하였지만 영어말고 다른 언어가 있을 때는 1바이트가 아닌 2바이트를 차지하고 있는 것이다.
그래서 문자가 1바이트만 차지한다고 생각하는 것을 잘못된 것이다.
사실상 const void* test라고 생각하면 된다.

UTF-16

auto test3 = u"aaa이백원입니다";
cout << test2 << endl;

이런식으로 사용할 수 있다.(u or L)

그래서 메모리를 확인해보면 3가지 방식모두다 다른 번호를 가지고 있는 것을 알 수 있다.

결국 온라인 통신이 들어가면 엄청 조심해야하고 싱글게임 이더라도 문자열을 세이브파일로 저장하게 된다면 문제가 생길 수도 있다.

어지간해서는 위에 구닥다리 방식보다는 유니코드 방식을 사용하는 것이 좋다.

여기서까지보면 왜 헷갈리지 라고 할 수 있는데 또 다른 방식이 있다.

MBCS vs WBCS

MBCS(Multi Byte Character Set) vs WBCS(Wide Byte Character set)

멀티바이트 집합 : 가변 길이 인코딩
유니코드 집합 : 고정 길이 인코딩

뭐가 멀티바이트 집합이고 유니코드 집합인지 보면
CP949와 UTF-8이 멀티바이트 집합이다. (1바이트 일수도 있지만 2바이트인 것도 있음)
하지만 유니코드 집합은 고정 길이이기에 UTF-16을 대표적인 예로 볼 수 있다.

하지만 이러한 것을 모르고 유니코드를 검색해서 찾아본다면 "유니코드는 무조건 2바이트 크기이고 고정길이 인코딩이다."라고 나와있지만 다른 문서를 또 보면 UTF-8은 영어는 1바이트 한국어는 3바이트라는 것을 보고 멘붕이 올 수 있다.

그래서 유니코드 집합을 WBCS(고정 길이 인코딩)로 인지를 하고,
유니코드 그 자체는 (동일 번호 = 동일한 문자 = 동일한 유니코드)를 말한다고 인지를 하면 편하다.

그래서 작업을 할 때도 일반적인 char나 string말고 wchar_twstring이라는 것이 있다. 여기서 w가 WBCS이다. 즉 UTF-16방식이라는 것을 알 수 있다.

실제로 사용할 때는

// L"어쩌구 저쩌구"
wchar_t ch = L'이';
wstring name = L'이백원';

wcout << name << endl; // 그냥 cout 말고 wcout사용

이런식으로 L을 붙여서 사용할 수 있다.

결국 우리가 게임을 만들 때 어떤걸로 문자열을 관리할 것인지 선택을 해야 한다.
보통 게임에서는 UTF-16이 합리적이라고 한다. (언리얼이나 유니티도 기본적으로 UTF-16방식으로 사용한다) 서버 클라가 통신할 때도 더 쉬운 부분이 많다.

강사님이 일했을 때 테라에서도 UTF-16방식으로 관리를 했다고 한다. (문자열 관리하거나 로그 찍을 때 앞에 L을 붙여서 사용했다)

결과적으로보면 헷갈릴만한 부분이다.
유니코드 집화과 유니코드를 혼동을 많이하고 어쩔때는 되지만 어쩔때는 깨지는 상황이 비일비제하게 발생할 수 있기에 헷갈릴수 있다..

C++이 이래서 참 안좋다..
C#같은 경우는 작업을 하면 거의 UTF-16을 사용하는 것과 같다. 그래서 신경을 쓸 필요가 없다. (그래서 C#은 char가 2바이트이다)

C++을 사용할 때는 처음부터 무슨 방식으로 사용할 것인지 정하고 많은 것을 생각해야 한다.

앞으로 만약 작업을 하게 된다면 UTF-16방식으로 작업을 하지만 const char*처럼 옛날 방식으로 받는 함수가 있기 때문에 가끔가다 Wide Byte에서 Multi Byte로 넘어가야 할 때가 있다.
그럴 때를 위해 헬퍼클래스를 만들어 와따리가따리 할 수 있게 만들어줘야지 편하게 작업을 할 수 있다.

마무리

클라 작업만 하게 된다면 많이 신경쓸 필요는 없지만,
서버 작업을 하게 된다면 이러한 부분을 발목을 잡을 수 있다.
가끔가다 서버 면접에서 문자집합, 유니코드 UTF-8, UTF-16을 이해하고 있는지 물어본다고 한다. (둘 다 유니코드이지만 인코딩이 다르다)

강사님이 일했을 때는 블&소는 UTF-8로 작업을 하였고 테라는 UTF-16으로 작업을 하였는데 UTF-8로 작업을 하게 되면 언리얼쪽에 보낸다음에 다시 한번더 UTF-16으로 바꿔치기 하는 작업이 필요했다.
그래서 UTF-16이 이런면에서는 좋은 것 같다.

뭔가 잘잊어버릴 것 같은 내용이기에 복습을 많이 해보자

profile
고3, 프론트엔드

0개의 댓글