Win32 C/C++ 문자열 종류와 상호 변환 방법
Win32 C/C++에는 문자열을 나타내는 자료형이 다양하다. 그리고 그 자료형마다 용도와 역할이 있기 때문에 어느 하나만을 선택해서 사용할 수는 없고, 적절한 형태로 변환하는 과정이 일반적으로 쓰인다. 본 시리즈에서는 Win32 C/C++에서 사용하는 문자열의 종류에 대해 설명하고 상호 변환 방법에 대해 설명하겠다.
1. char, char *, const char *
C/C++은 기본적으로 ASCII 코드로 인코딩된 ANSI 문자열을 지원한다. 가장 기본적인 문자와 문자열의 형태이다.
- char
char
는 ASCII로 인코딩된 문자 하나를 표현하는 자료형으로서 1바이트의 크기를 갖는다.
- char *
char *
는 ASCII 문자로 구성되며 항상 끝이 1바이트의 NULL 문자로 끝나는 문자열을 나타내는 자료형이다.
- const char *
const char *
는 ASCII 문자로 구성되며 항상 끝이 1바이트의 NULL 문자로 끝나는 문자열 상수를 나타내는 자료형이다.
문자열과 문자열 상수에 대해 좀 더 설명하자면, 둘 다 끝이 NULL
문자로 끝나야 하는 특징이 있지만 문자열은 중간의 일부 문자를 변경할 수 있고 문자열 상수는 변경이 불가하다. 또한 문자열은 주로 버퍼에 복사된 문자열을 뜻하고, 문자열 상수는 주로 소스 코드를 작성할 때 처음부터 정해진 문자열을 뜻한다. 소스 코드에서부터 정해진 문자열은 실행파일 내에 내장되고, 프로그램이 적재될 때 함께 메모리에 로드되며 이는 프로그램이 진행되는 내내 변경될 수 없다.
1-1. Multibyte ANSI String
영어권 이외의 국가에서는 자국의 문자를 표현하기 위해 확장 ASCII 영역을 새롭게 정의해서 사용한다. 그러나 이것으로도 글자를 표현하기에 부족한 경우(예: 완성형 한글 및 한자)에는 확장 ASCII 문자 2개를 묶어서 자국의 문자를 할당하기도 하였다. 여기에서 영문자는 1바이트, 한글/한자는 2바이트 크기라는 규칙이 생겼다. 이러한 인코딩으로 된 문자열을 부르는 명칭에는 여러가지가 있겠으나 여기에서는 Multibyte ANSI String이라 하겠다.
Multibyte ANSI String도 컴퓨터의 입장에서는 ASCII 코드이므로 char *
, const char *
형으로 표현가능하다. 단 자국 문자는 기본 2바이트이기 때문에 1 문자를 char 단독으로 저장할 수 없다. 예를 들어,
char a = 'A'; // 가능하다.
char b = '가'; // 불가능하다.
2. wchar_t, wchar_t *, const wchar_t *
영어권 이외의 국가를 고려하여 모든 문자가 n바이트의 크기를 갖는 Widechar 문자열을 지원하는 형식이다. Multibyte가 문자의 종류마다 1 문자당 크기(바이트)가 달라졌다면, Widechar는 모든 문자가 1 바이트 이상의 동일한 크기를 갖는 차이가 있다. 일반적으로 Windows 운영체제에서는 wchar_t의 크기가 2 바이트이며 UCS-2로 인코딩된다. Linux 운영체제에서는 wchar_t의 크기가 4 바이트이며 UCS-4로 인코딩되지만 컴파일러 설정에 따라 Windows 방식과 동일하게 변경 가능하다. (그러나 wchar_t를 쓸 경우 반드시 유니코드로 처리된다는 규칙은 없으므로 코딩에 참고하기 바란다.)
- wchar_t
wchar_t
는 Widechar로 표현된 1개 문자이다.
- wchar_t *
wchar _t *
는 Widechar로 표현된 문자열이며 1바이트 이상의 크기를 갖는 NULL
문자로 끝난다.
- const wchar_t *
const wchar_t *
는 wchar_t *
와 동일하지만 상수 특성을 갖는다.
2-1. 문자열 리터럴 접두어
문자열 상수를 유니코드 문자열로 정의하기 위해서는 문자열 앞에 접두어를 붙이면 된다. C++11에서 정의된 문자열 리터럴literal 접두어의 종류로는 아무것도 안 붙임, L
, u8
, u
, U
의 5가지가 있다.
- "문자열"
- 표준 ASCII 문자 및 Multibyte 문자로 구성된 ANSI 문자열이다.
- L"문자열"
- Widechar 문자로 구성된 문자열이다. 일반적으로 유니코드로 인코드된다.
이외의 문자열에 대해서는 다음 문단에서 이어진다.
3. 유니코드 문자로서의 char, char16_t, char32_t
char
는 경우에 따라 ASCII 문자를 나타낼 때도 쓰이지만 유니코드(UTF-8) 문자를 나타낼 때도 쓰인다. 이는 일반적인 로마자에 대하여 ASCII와 Unicode가 호환되기 때문이다. 물론 로마자 이외의 문자는 유니코드로 표현된 char라고 해도 문자의 종류에 따라 Multibyte처럼 여러 바이트에 걸쳐져서 표현된다.
char16_t
와 char32_t
는 C++11부터 도입된 자료형으로 uchar.h
또는 cuchar
에 정의되어 있습니다. 각각 UTF-16과 UTF-32로 인코드된 문자 및 문자열을 표현할 때 사용된다.
2-1. 유니코드 문자열 리터럴의 접두어
문자열 상수를 유니코드 문자열로 인코드 하고자 할 때는 u8
, u
, U
의 세가지 중 하나를 사용한다.
- u8"문자열"
- 문자열 상수를 UTF-8로 저장한다. 이 때는
char
자료형으로 문자와 문자열을 표현한다.
- u"문자열"
- 문자열 상수를 UTF-16으로 저장한다. 이 때는
char16_t
자료형으로 문자와 문자열을 표현한다.
- U"문자열"
- 문자열 상수를 UTF-32로 저장한다. 이 때는
char32_t
자료형으로 문자와 문자열을 표현한다.
4. Multibyte - Widechar간 상호 변환
지금까지는 C/C++ 표준에 정의된 자료형에 대한 설명이었다. 위 세 종류의 자료형으로된 문자열 및 문자열 상수를 상호변환해 보겠다.
Multibyte(ANSI 문자의 char 형)와 Widechar(wchar_t 형)의 상호 변환은 wctomb
, mbtowc
, wcstombs
또는 mbstowcs
를 사용한다. 전자는 1개 문자를 변환하고 후자는 문자열을 변환하며 stdlib.h
또는 cstdlib
헤더파일에 정의되어 있다.
int mbtowc (wchar_t * pwc, const char * pmb, size_t max); // Multibyte에서 Widechar로 1개 문자를 변환
size_t mbstowcs (wchar_t * dest, const char * src, size_t max); // Multibyte에서 Widechar로 문자열을 변환
int wctomb(char * pmb, wchar_t wc); // Multibyte에서 Widechar로 1개 문자를 변환
size_t wcstombs(char * dest, const wchar_t * src, size_t max); // Multibyte에서 Widechar로 문자열을 변환
4-1. Wide Character에서 Multibyte Character로 변환
다음은 1개의 문자를 Widechar에서 Multibyte로 변환하는 예이다.
/* C++ Source */
#include <cstdlib>
#include <cassert>
#include <clocale>
using namespace std;
/* ... */
wchar_t wc = L'가'; // 변환할 1개의 Wide Character
char mbc[8] = { 0, }; // 인코딩을 고려할 때 1개의 Wide Character에 대한 충분한 Multi Byte 공간을 제공합니다. 대체로 1개 문자당 8 바이트를 부여하면 충분하다.
int mbn = 0; // 해당 문자가 Multibyte Character로 변환시 실제로 몇 바이트를 차지하는지 확인한다.
setlocale(LC_ALL, ""); // 변환에 앞서 프로그램의 로케일을 시스템에서 기본으로 설정한 로케일로 리셋한다.
/* 1개의 Wide Character를 Multibyte Character로 변환한다. */
/* 이 때 변환된 Multibyte Character의 바이트 수를 반환받는다. */
/* 지정한 Wide Character가 현재의 로케일에서 표현할 수 없는 문자일 때, -1이 반환된다. */
assert((mbn = wctomb(mbc, wc)) != -1);
printf("Wide Character = %lc [%d byte(s)]\n", wc, sizeof(wc));
printf("Multibyte Character = %s [%d byte(s)]\n", mbc, mbn);
/* ... */
4-2. Wide String에서 Multibyte String으로 변환
다음은 Wide 문자열(wchar_t *)에서 Multibyte 문자열(char */ANSI)로 변환하는 예이다.
/* C++ Source */
#include <cstdio>
#include <cstdlib>
#include <cassert>
#include <clocale>
using namespace std;
/* ... */
wchar_t wcs[] = L"안녕하세요."; // 변환할 Wide String
char mbs[112] = { 0, }; // 인코딩을 고려할 때 1개의 Wide Character에 대한 충분한 Multi Byte 공간을 제공합니다. 대체로 1개 문자당 8 바이트를 부여하면 남아돕니다.
int mbn = 0; // 해당 문자가 Multibyte Character로 변환시 실제로 몇 바이트를 차지하는지 확인합니다.
setlocale(LC_ALL, ""); // 변환에 앞서 프로그램의 로케일을 시스템에서 기본으로 설정한 로케일로 리셋합니다.
/* Wide String을 Multibyte String으로 변환한다. */
/* 이 때 변환된 Multibyte String의 바이트 수를 반환받는다. */
/* 지정한 Wide Character가 현재의 로케일에서 표현할 수 없는 문자일 때, ((size_t)(-1))이 반환된다. */
/* ((size_t)(-1))은 컴파일러에 따라 0xFFFF, 0xFFFFFFFF, 0xFFFFFFFFFFFFFFFF 중 하나를 말한다. */
assert((mbn = wcstombs(mbs, wcs, sizeof(mbs))) != ((size_t)(-1)));
printf("Wide String = %ls [%d byte(s)]\n", wcs, sizeof(wcs));
printf("Multibyte Character = %s [%d byte(s)]\n", mbs, mbn);
/* ... */
4-3. Multibyte Character에서 Wide Character으로 변환
다음은 Multibyte Character를 Wide Character로 변환하는 예이다.
/* C++ Source */
#include <cstdio>
#include <cstdlib>
#include <cassert>
#include <clocale>
using namespace std;
/* ... */
char mbc[] = "가"; // 1개의 문자를 n바이트에 저장
size_t mbn = sizeof(mbc) - 1; // 끝의 NULL을 제외한 순수 1글자의 크기(바이트)
wchar_t wc[4] = { L'\0', }; // 1개의 문자를 1개의 Widechar에 저장, Surrogate로 변환될 것을 가정하여 1문자당 최대 4개의 wchar_t를 확보해 놓으면 넉넉하다.
setlocale(LC_ALL, ""); // 변환에 앞서 프로그램의 로케일을 시스템에서 기본으로 설정한 로케일로 리셋한다.
/* Multibyte Character를 Wide Character로 변환한다. */
/* 이 로케일에서 정의하는 문자가 Wide Character로 표현될 수 없을 경우 -1을 반환한다. */
assert(mbtowc(wc, mbc, mbn) != -1);
printf("Multibyte Character = %s [%d byte(s)]\n", mbc, mbn);
printf("Wide Character = %ls [%d byte(s)]\n", wc, sizeof(wc));
/* ... */
4-4. Multibyte String에서 Wide String으로 변환
다음은 Multibyte 문자열을 Widechar 문자열로 변환하는 예이다.
/* C++ Source */
#include <cstdio>
#include <cstdlib>
#include <cassert>
#include <clocale>
/* ... */
char mbs[] = "안녕하세요."; // 변환할 Multibyte String
wchar_t wcs[28] = { L'\0', }; // 인코딩을 고려할 때 1개의 Multibyte Character에 대한 충분한 Wide String 공간을 제공한다. Surrogate로 변환될 것을 가정하여 1개 문자당 최대 4개의 wchar_t를 확보해 놓으면 넉넉하다.
size_t wcn = 0; // 실제로 변환되어 생성된 문자 수를 받아온다.
setlocale(LC_ALL, ""); // 변환에 앞서 프로그램의 로케일을 시스템에서 기본으로 설정한 로케일로 리셋한다.
/* Multibyte String를 Wide String으로 변환한다. */
/* 이 로케일에서 정의하는 문자가 Wide String으로 표현될 수 없을 경우 -1을 반환한다. */
assert((wcn = mbstowcs(wcs, mbs, sizeof(wcs) / sizeof(wchar_t))) != ((size_t)(-1)));
/* ... */
4-5. (덧) Multibyte String과 Wide String의 문자 개수 세는 방법
strlen
함수는 문자열을 구성하는 모든 문자가 표준 ASCII 코드로 표현된 ANSI 문자일 경우에 유효하다. 그러나 KS 완성형 코드나 JIS 코드 등 특정 인코딩을 사용하는 시스템일 경우 strlen
함수가 반환하는 문자의 수가 실제와 다르다. Multibyte String일 때 사용하는 함수는 mblen
이다.
int mblen (const char* pmb, size_t max);
- pmb
- 문자의 개수를 구하고자 하는 Multibyte String이다.
- max
pmb
버퍼의 최대 크기이다.
Wide String의 문자수를 세는 함수는 wcslen
이다. 사용법은 strlen
과 같다.
5. Wide Character - Unicode간 상호 변환
Wide Character/String과 Unicode간 변환을 지원하는 표준 함수는 이 포스트가 작성된 현재 없다. 이 경우 다음에 설명되는 Windows API를 사용한다.
6. Multibyte Character - Unicode간 상호 변환
Multibyte Character와 UTF-16/UTF-32간 변환은 표준 함수로 제공된다. 그 외 UTF-7/UTF-8은 이 포스트가 작성된 현재 표준에 없다. 이 경우 다음에 설명되는 Windows API를 사용한다.
6-1. Multibyte Character에서 UTF-16으로 변환
Multibyte Character에서 UTF-16 Character로 변환하기 위해서는 mbrtoc16
함수를 사용합니다.
/* C++ Source */
#include <cstdio>
#include <cstdlib>
#include <cassert>
#include <clocale>
#include <cuchar>
using namespace std;
/* ... */
char mbc[8] = "가";
char16_t uc[8] = { '\0', };
mbstate_t mbstate = { 0 };
setlocale(LC_ALL, "");
assert((mbn = mbrtoc16(uc, mbc, sizeof(mbc), &mbstate)) != (size_t)(-1));
/* ... */
6-2. UTF-16에서 Multibyte Character으로 변환
UTF-16 Character에서 Multibyte Character로 변환하기 위해서는 c16rtomb
함수를 사용한다.
/* C++ Source */
#include <cstdio>
#include <cstdlib>
#include <cassert>
#include <clocale>
#include <cuchar>
using namespace std;
/* ... */
char16_t uc = u'가';
char mbc[8] = { '\0', };
size_t mbn = 0;
mbstate_t mbstate = { 0 };
setlocale(LC_ALL, "");
assert((mbn = c16rtomb(mbc, uc, &mbstate)) != (size_t)(-1));
/* ... */
6-3. Multibyte Character에서 UTF-32으로 변환
Multibyte Character에서 UTF-32 Character로 변환하기 위해서는 mbrtoc32
함수를 사용한다.
/* C++ Source */
#include <cstdio>
#include <cstdlib>
#include <cassert>
#include <clocale>
#include <cuchar>
using namespace std;
/* ... */
char mbc[8] = "가";
char32_t uc[8] = { U'\0', };
mbstate_t mbstate = { 0 };
setlocale(LC_ALL, "");
assert((mbn = mbrtoc32(uc, mbc, sizeof(mbc), &mbstate)) != (size_t)(-1));
/* ... */
6-4. UTF-32에서 Multibyte Character으로 변환
UTF-32 Character에서 Multibyte Character로 변환하기 위해서는 c32rtomb
함수를 사용한다.
/* C++ Source */
#include <cstdio>
#include <cstdlib>
#include <cassert>
#include <clocale>
#include <cuchar>
using namespace std;
/* ... */
char32_t uc = U'가';
char mbc[8] = { '\0', };
size_t mbn = 0;
mbstate_t mbstate = { 0 };
setlocale(LC_ALL, "");
assert((mbn = c32rtomb(mbc, uc, &mbstate)) != (size_t)(-1));
/* ... */
7. 마무리
여기까지 해서 Win32 C/C++ 문자열 중 C/C++ 표준 문자열 타입에 대해 알아보았다. 다음 포스트에서는 Windows API에서 정의된 기본 문자열 타입에 대해 알아보겠다.