85.22 wchar_t 사용하기

wchar_t는 와이드 문자(wide character)를 저장하기 위한 자료형입니다. 보통 영문 알파벳은 1바이트로 표현하지만 유니코드는 2바이트 이상으로 표현하기 때문에 wchar_t에 저장해야 합니다.

Visual Studio에서는 wchar_tunsigned short로 정의하고 있고, GCC에서는 4바이트로 정의하고 있습니다. 단, GCC에서 -fshort-wchar 옵션을 사용하면 wchar_t를 2바이트로 사용할 수 있습니다.

wchar_t 문자와 문자열은 다음과 같이 따옴표 앞에 L을 붙여서 표현합니다.

wchar_t wc1 = L'a';

wchar_t *ws1 = L"Hello, world!";    // 와이드 문자열은 L을 붙임
wchar_t *ws2 = L"안녕하세요.";

다음과 같이 wchar_t 문자열은 w가 붙은 함수를 사용해야 합니다(wchar.h 헤더 파일에 선언되어 있습니다).

wchar1.c

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <wchar.h>

int main()
{
    wchar_t ws1[20];
    wscanf(L"%s", ws1);
    wprintf(L"%s\n", ws1);

    // 두 번째 인수에는 wchar_t 문자열의 문자 개수를 구해서 넣어줌
    swprintf(ws1, sizeof(ws1) / sizeof(wchar_t), L"%s", L"Hello, world!");

    FILE *fp = _wfopen(L"hello.txt", L"w+");
    // _wfopen 함수는 표준이 아니므로 리눅스에서는 사용할 수 없음
    // 리눅스에서는 fopen 함수 사용
    // FILE *fp = fopen("hello.txt", "w+");

    // 리눅스에서는 %s 대신 %S 사용
    wchar_t ws2[20];
    wchar_t ws3[20];
    fwprintf(fp, L"%s", L"C Language");
    rewind(fp);
    fwscanf(fp, L"%s %s", ws2, ws3);
    wprintf(L"%s %s\n", ws2, ws3);

    rewind(fp);
    fputws(L"Wide Character", fp);
    rewind(fp);
    fgetws(ws2, 20, fp);
    wprintf(L"%s\n", ws2);

    rewind(fp);
    fputwc(L'X', fp);
    rewind(fp);
    wint_t wc1 = fgetwc(fp);
    putwchar(wc1);

    fclose(fp);

    return 0;
}

실행 결과

Hello (입력)
Hello
C Language
Wide Character
X

swprintf 함수는 swprintf(ws1, sizeof(ws1) / sizeof(wchar_t), L"%s", L"Hello, world!");와 같이 버퍼와 함께 wchar_t 문자열의 문자 개수를 넣으면 됩니다. 여기서 wchar_t ws1[20];일 때 sizeof(ws1)만 지정하면 wchar_t 크기가 2바이트이므로 40이 나옵니다. 따라서 sizeof(ws1)sizeof(wchar_t)로 나누어서 문자 개수를 구하도록 만들어야 합니다.

_wfopen 함수는 표준이 아니며 Visual Studio에서만 사용할 수 있고 리눅스에서는 사용할 수 없습니다.

wchar_t 문자열 처리 함수는 str 대신 wcs로 시작합니다. 예를 들어 strcpy의 와이드 문자 버전은 wcscpy입니다.

wchar2.c

#define _CRT_SECURE_NO_WARNINGS
#include <wchar.h>

int main()
{
    wchar_t ws1[20] = L"Hello, world!";
    wchar_t ws2[20];

    wprintf(L"%d\n", wcslen(ws1));

    // 리눅스에서는 %s 대신 %S 사용
    wcscat(ws1, L"123");
    wprintf(L"%s\n", ws1);

    wcscpy(ws2, ws1);
    wprintf(L"%s\n", ws2);

    wprintf(L"%d\n", wcscmp(ws1, ws2));

    wchar_t *ptr = wcschr(ws1, L'o');
    wprintf(L"%s\n", ptr);
    
    ptr = wcsrchr(ws1, L'l');
    wprintf(L"%s\n", ptr);

    ptr = wcsstr(ws1, L"wor");
    wprintf(L"%s\n", ptr);

    wchar_t ws3[20] = L"The Little Prince";
    wchar_t *next;
    ptr = wcstok(ws3, L" ", &next);    // next에는 다음번에 자를 문자열의 포인터가 들어감

    while (ptr != NULL)
    {
        wprintf(L"%s\n", ptr);
        ptr = wcstok(NULL, L" ", &next);
    }

    return 0;
}

실행 결과

13
Hello, world!123
Hello, world!123
0
o, world!123
ld!123
world!123
The
Little
Prince

리눅스에서 wprintfwchar_t 문자열을 출력할 때는 %s 대신 %S를 사용하면 됩니다. 참고로 C 언어에서 다국어 및 유니코드 처리는 매우 까다로운 분야입니다. 여기서는 이런 자료형과 함수가 있다는 것 정도만 알아두면 됩니다.

참고 | ICU

실제로 전문적인 소프트웨어는 IBM이 오픈 소스로 공개한 ICU(International Components for Unicode) 라이브러리를 많이 사용합니다. 한글화가 잘 된 게임이라면 ICU를 사용하고 있을 가능성이 큽니다. 시드 마이어의 <문명 V>, 오픈 오피스, 이클립스, Node.js, 파이썬, OS X, iOS, 사파리, 안드로이드, 크롬, 구글 지도 등 많은 제품이 ICU를 사용합니다. 다국어 지원이 필요한 소프트웨어를 개발한다면 ICU 같은 전문적인 라이브러리를 검토해보세요.