관리 메뉴

(코딩캣) = "코딩"하는 고양이;

Win32 문자열 종류와 상호 변환 방법 #1 본문

Application Programming Interface/Windows API

Win32 문자열 종류와 상호 변환 방법 #1

컴파일러님, 이 코드는 고양이발로 작성되었습니다. 코딩집사 2018.03.05 08:44

Win32 C/C++ 문자열 종류와 상호 변환 방법 #1

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 문자를 나타낼 때도 쓰이지만 유니코드 문자를 나타낼 때도 쓰입니다. 이는 일반적인 로마자에 대하여 ASCII와 Unicode가 호환되기 때문입니다. 물론 로마자 이외의 문자는 유니코드로 표현된 char라고 해도 문자의 종류에 따라 Multibyte처럼 여러 바이트에 걸쳐져서 표현됩니다.

char16_tchar32_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로 변환하는 예입니다.

/* Example */
#include <stdlib.h>
#include <assert.h>
#include <locale.h>

/* ... */

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)로 변환하는 예입니다.

/* Example */
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <locale.h>

/* ... */

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로 변환하는 예입니다.

/* Example */
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <locale.h>

/* ... */

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 문자열로 변환하는 예입니다.

/* Example */
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <locale.h>

/* ... */

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 함수를 사용합니다.


#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <locale.h>
#include <uchar.h>

/* ... */

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 함수를 사용합니다.


#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <locale.h>
#include <uchar.h>

/* ... */

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 함수를 사용합니다.


#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <locale.h>
#include <uchar.h>

/* ... */

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 함수를 사용합니다.


#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <locale.h>
#include <uchar.h>

/* ... */

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에서 정의된 기본 문자열 타입에 대해 알아보겠습니다.

 

 

2 Comments
댓글쓰기 폼