Standard output으로 unicode 문자를 출력하기 (Win32 console application)

1 minute read

Win32 console application에서 Unicode 문자를 표준 출력을 통해 출력하려고 한다. 그러나 console window가 Unicode를 지원하지 않는 문제점이 있다. 이런 문제점을 해결하려면 locale 설정을 한 후 출력을 한다.

#include <iostream>
#include <locale>

int main(int argc, wchar_t* argv[])
{
    std::wcout.imbue( std::locale("kor") ); // locale 설정.
    std::wcout << L'\xBDC1' << L'\x0424' << L'\x3048' << L'\x7E6D' << std::endl;
    if( std::wcout.fail() )
    {
        std::wcerr << L"failed : print unicode characters" << std::endl;

        return 1;
    }

    return 0;
}

nil

한글로 로케일을 설정했다. 이렇게 하면 Unicode 문자가 Codepage 949 문자로 변환된 다음 출력이 되게 된다.

그럼 Codepage 949로 표현하지 못하는 Unicode 문자를 출력하면 어떻게 될까?

std::wcout << L'\x0D30' << std::endl;

nil

Codepage 949 문자로 변환이 실패해서 fail bit가 설정된다.

nil

제길 Unicode를 제대로 출력하려면 문자의 code point를 검사해서 Unicode 문자를 표현할 수 있는 Codepage로 locale설정을 한 다음 출력해야 하나? 이쯤되면 유니코드 출력에 wcout을 포기하는게 나은 선택이 될 것이다.

다행인게 VS2005부터 Unicode 표준 출력을 지원한다.

#include <iostream>
#include <locale>
#include <io.h>
#include <fcntl.h>

int main(int argc, wchar_t* argv[])
{
    _setmode(_fileno(stdout), _O_U16TEXT);

    std::wcout << L'\x0D30' << std::endl;

    return 0;
}

nil

야호!!! 잘 출력된다. 그런데 좀 출력이 이상하다. fail bit를 검사해보니 한 문자를 출력할 때마다 세팅되고 있다. 물론 출력은 잘 되고 있는데 말이다.

std::wcout << L'\xBDC1';
std::wcout.clear();
std::wcout << L'\x0424';
std::wcout.clear();
std::wcout << L'\x3048';
std::wcout.clear();
std::wcout << L'\x7E6D';
std::wcout.clear();
std::wcout << L'\x0D06';
std::wcout.clear();
std::wcout << std::endl;

한 문자를 출력하고 난 뒤, 상태 비트를 매번 클리어 시켜줘야 다음 문자가 제대로 출력된다.

nil

이것 참! 원인을 모르겠네. 이거 원인을 알아내기 전까지는 속 편히 WriteConsoleW을 써야겠다. 아님 현재 Codepage에서 다 보여줄 수 있다는 확신을 갖고 로케일 설정을 해서 쓰거나~

#include <windows.h>
#include <string>
int main(int argc, wchar_t* argv[])
{
    std::wstring    unicodeString = L"\xBDC1\x0424\x3048\x7E6D\x0D06";

    // 문자수를 이렇게 계산하면 안 된다.  UTF-16은 한 캐릭터가 2byte 혹은 4byte이다.
    // 테스트니깐 뭐 어때~
    const DWORD charCount = static_cast<DWORD>(unicodeString.length());
    DWORD writtenCount;
    WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE),
        unicodeString.c_str(), charCount, &writtenCount, NULL);

    return 0;
}

nil

PS : 콘솔에서 폰트가 이상하게 출력되는 이유는 사용 폰트를 Arial Unicode MS를 설정해서 그렇다.

PS2 : 에잉! 제일 앞에서 U+0D30을 사용하고 뒤에서는 U+0D06을 사용했다. 출력 여부가 중요하니 그냥 넘어간다.