UDN
Search public documentation:

CharacterEncodingKR
English Translation
日本語訳
中国翻译

Interested in the Unreal Engine?
Visit the Unreal Technology site.

Looking for jobs and company info?
Check out the Epic games site.

Questions about support via UDN?
Contact the UDN Staff

UE3 홈 > 엔진 프로그래밍 > 캐릭터 인코딩

캐릭터 인코딩


문서 변경내역: 잭 포터 작성.

개요


언리얼에서 사용되는 캐릭터 인코딩에 대한 개요입니다.

사전 지식: 모든 소프트웨어 개발자들이 절대적으로, (예외 없이!)반드시 최소한으로 알아야 하는 유니코드와 캐릭터 세트

텍스트 포맷


텍스트와 스트링을 표현하는 데 사용할 수 있는 포맷은 여럿 있습니다. 이러한 포맷을 이해하고 그에 따른 장단점을 이해하는 것은 프로젝트에 어떤 포맷을 사용할 것인지 결정하는 데 도움이 될 수 있습니다.

이 내용은 포맷의 기술적인 정의는 아니며, 단지 이 페이지에 걸맞는 단순화된 버전입니다.

ASCII
0, 9, 10, 13, 32 - 126 를 포함한 캐릭터입니다. (P4 타입 텍스트) (이는 체크인시 P4 트리거로 검증됩니다.)
ANSI
ASCII 와 (서유럽 하이 ASCII 등) 현재 코드 페이지 (P4 서버에는 바이너리로 저장)
UTF-8
(ASCII 상위집합인) 비-ANSI 캐릭터를 구하기 위해 특수 캐릭터 시퀸스를 사용할 수 있는 단일 바이트로 구성된 스트링 입니다. (P4 타입 Unicode)
UTF-16
(아스트랄 캐릭터 포함 4 바이트가 될 수는 있지만) BOM(Byte Order Mark) 포함 글자 당 2 바이트로 구성된 스트링입니다. (P4 타입 UTF-16) (체크인시 P4 트리거로 검증됩니다.)

바이너리의 경우

장점 단점
내부 포맷이 정의되지 않습니다. 각 파일을 포맷에 관계없이 로드할 수 있습니다. 머지할 수 없습니다. 이런 종류의 파일 전부를 독점 체크아웃해야 합니다.
  내부 포맷은 정의되지 않습니다. 각 파일의 포맷이 다를 수 있습니다.
  P4 는 각 버전 전체를 저장하므로, 디포 크기가 불필요하게 불어날 수 있습니다.

텍스트의 경우

장점 단점
머지할 수 있습니다. 독접 체크아웃할 필요가 없습니다. 매우 제한적입니다. ASCII 캐릭터만 허용됩니다.

UTF-8 의 경우

장점 단점
필요한 모든 문자에 간단히 접근할 수 있습니다. 아시아 언어에는 메모리 프로파일이 다릅니다.
메모리를 적게 사용합니다. P4 타입 유니코드는 저희 퍼포스 서버에 활성화되어 있지 않습니다.
ASCII 상위집합입니다. 일반 ASCII 스트링 그대로가 완벽히 UTF-8 스트링이 됩니다. 스트링 연산이 더 복잡합니다. 길이 계산같은 간단한 연산을 할래도 스트링을 파싱해야 합니다.
스트링이 ASCII 인 이고 그대로 출력하는 것으로 감지되어도 작동합니다. MSDev 는 아시아 지역에서 ASCII 이외의 것을 처리하지 못합니다. 그래서 체크인 도중 텍스트가 ASCII 인지 검사합니다.
유니코드가 활성화된 서버가 있다면 파일을 머지할 수 있어 독점 체크아웃할 필요가 없습니다.  
파싱을 통해 스트링이 (BOM 있든 없든) UTF-8 인지 알아낼 수 있습니다.  

UTF-16 의 경우

장점 단점
필요한 모든 글자에 간단히 접근할 수 있습니다. 메모리를 더 사용합니다.
단순합니다. 메모리 사용량은 글자 수의 두 배입니다. (사용하는 글자는 모두 Basic Multilingual Plane) 에 있습니다. BOM 이 없는 포맷인지 알기 어렵습니다.
단순합니다. 파싱할 필요 없이 스트링을 나누고/합치는 연산이 가능합니다. 스트링이 ASCII 이고 그대로 출력하는 것으로 감지되면 작동하지 않습니다. (이제 UTF-16 검증과 함께 체크인시 검사됩니다.)
게임에서 사용된 포맷과 같아, 변환이나 파싱이나 메모리 연산 등이 필요치 않습니다. MSDev 는 아시아 지역에서 ASCII 이외의 것을 잘 처리하지 못합니다. 그래서 체크인 도중 텍스트가 ASCII 인지 검증하는 것입니다.
머지할 수 있습니다. 독점 체크아웃할 필요가 없습니다.  
C# 은 내부적으로 UTF-16 을 사용합니다.  

UE3 내부 스트링 표현


언리얼 엔진3 의 모든 스트링은 FStrings 혹은 TCHAR 정렬 상태로 UTF-16 포맷 메모리에 저장됩니다. 대부분의 코드에서 2 바이트가 하나의 코드포인트라 가정하므로, 언리얼의 내부 인코딩이 UCS-2 로 보다 정확히 설명될 수 있도록 Basic Multilingual Plane(BMP) 만 지원됩니다. 스트링은 현 플랫폼에 적합한 엔디안에 저장됩니다.

디스크에/서나 네트워킹 도중 패키지를 Serialize 할 때, 0xff 보다 작은 모든 TCHAR 캐릭터 스트링은 8 비트 바이트 시리즈로 저장되고, 그 외에는 2 바이트의 UTF-16 스트링으로 저장됩니다. Serialize 코드는 필요에 따라 어떤 엔디안 변환도 다룰 수 있습니다.

UE3 에 로드되는 텍스트 파일


언리얼이 외부 텍스트 파일을 로드할 때(, 예로 실행시간에 .INT 파일을 읽을 때나 언리얼스크립트 컴파일러가 언리얼스크립트 .uc 소스 파일을 로드할 때)는, 거의 항상 UnMisc.cpp 에서 찾을 수 있는 appLoadFileToString() 함수로 해결합니다. 작업은 주로 appBufferToString() 함수에서 일어납니다.

이 함수는 UTF-16 파일에서 유니코드 바이트-오더-마크(byte-order-mark, BOM)를 인식하고, 있으면 어느 엔디안으로든 UTF-16 으로 파일을 로드합니다.

BOM 마크가 없을 때 발생하는 일은 플랫폼에 따라 달라집니다.

윈도우에서 그것은 디폴트 윈도우 MBCS 인코딩을 사용하여 텍스트를 UTF-16로 변환하려고 시도할 것이고 (eg 미국식 영어, 서유럽은 Windows-1252, 한국어는 CP949, 그리고 일본어는 CP932) MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS...) 를 사용합니다. 이것은 2009년 7월 QA 빌드 쯤에 추가되었습니다.

만약 이 변환이 실패하거나 혹은 윈도우 외의 플랫폼에서 실패한다면, 그것은 그저 각각의 바이트를 읽고 16비트로 패드하여 TCHAR 정렬을 만듭니다.

appLoadFileToString() 으로 로드된 UTF-8 인코드 텍스쳐 파일을 감지하거나 디코드하는 코드가 없음을 명심하십시오.

언리얼에 의해 저장되는 텍스트 파일


엔진에 의해 생성되는 대부분의 텍스트 파일은 appSaveStringToFile() 을 사용하여 저장될 것입니다.

단일한 바이트로 표현가능한 TCHAR 캐릭터로 된 스트링은 일련의 8 비트 바이트로서 저장될 것이고, 그렇지 않으면 bAlwaysSaveAsAnsi 플래그가 TRUE로 패스되는 경우, 즉 디폴트 윈도우 인코딩으로 먼저 변환될 수 있는 경우가 아닌 이상 UTF-16으로서 저장될 것입니다. 이것은 현재 셰이더 파일에서만 행해지고 있으며 셰이더 컴파일러가 UTF-16 파일과 관련하여 가지고 있는 이슈를 해결하기 위함입니다.

언리얼에서 사용되는 텍스트 파일을 위한 추천 인코딩


INT 와 INI 파일

두 쪽 중 어느 엔디안에서의 UTF-16. 아시아 언어에 대한 디폴트 MBCS 인코딩이 윈도우 상에서 작동할 동안, 이 파일들은 PS3와 Xbox360 상에서 로드 될 필요가 있고 변환 코드는 윈도우 상에서만 작동합니다.

소스 코드

일반적으로 우리는 C++ 혹은 UnrealScript 소스 코드 내의 스트링 문자를 추천하지 않고 이 데이터가 INT 파일로 가는 것을 추천합니다.

UnrealScript 소스 코드

UTF-16 혹은 디폴트 윈도우 인코딩 입니다. 두 경우에 있어, 일본어와 한국어 스트링 문자와 코멘트는 정확하게 작동해야 하는데, 이는 컴파일러가 윈도우 상에서 작동하고 텍스트를 UTF-16에 내부적으로 저장할 언리얼 패키지를 생성하기 때문입니다. 만약 당신이 디폴트 윈도우 인코딩을 사용한다면 이 .uc 파일이 다른 지역어로 된 기계에선 작동하지 않을 것임을 명심해야 합니다.

C++ 소스

UTF-8 혹은 디폴트 윈도우 인코딩 입니다. MSVC, Xbox360 컴파일러 와 gcc 모두 UTF-8 인코딩된 소스 파일로 괜찮을 것입니다. 높은 비트 셋, 예를 들어 저작권, 트레이드 마크 혹은 각도 표시가 잇는 캐릭터로 된 Latin-1 인코드 파일은 가능한 소스 코드에서 지양되어야 하는데, 이는 다른 지역어로 된 시스템에서는 인코딩이 깨질 것이기 때문입니다. 써드파티 소프트웨어 에서 이것의 몇몇 경우들은 불가피하여 (eg 저작권 공지) MSVC에 대해서, 아시아 윈도우 상에서 컴파일 할 때 발생할 수 있는 4819 경고문을 비활성화 시킵니다.

퍼포스에 UTF-16 테스트 파일 저장하기

  • 'Text' 를 사용하지 마십시오
    • 만약 UTF-x 파일이 체크되고 텍스트로 저장된다면, 싱크 후에 망가지게 될 것입니다.
  • 만약 'Binary' 를 사용하게 된다면, 파일을 익스클루시브 체크아웃(exclusive checkout)으로 표시하십시오
    • ASCII, UTF-8, UTF-16 에 체크할 수 있고, 그것은 엔진에서 작동할 것입니다.
    • 그러나 binary 파일은 합쳐질 수 없기 때문에, 만약 파일이 익스클루시브 체크아웃으로 표시되지 않으면 변화가 들쑥날쑥 생길 것입니다.
  • 'UTF-16' 를 사용한다면, UTF-16이 아닌 파일에 누군가가 체크하지 않음을 확실히 하십시오
    • 우리는 퍼포스 트리거를 가지고 있는데 이는 UTF-16에서와 같이 non UTF-16 에서 체킹을 허용하지 않습니다
      • //depot/UnrealEngine3/Development/Tools/P4Utils/CheckUTF16/
  • 'Unicode' 타입은 UTF-8 이고, 여기서는 우리에게 쓸모가 없습니다.

변환 루틴


우리는 스트링을 다양한 인코딩으로, 그리고 인코딩으로부터 변환할 수 있는 많은 매크로를 가지고 있습니다. 이 매크로는 로컬 범위에서 디클레어된 계층 인스턴스를 사용하고 스페이스를 스택(stack)에 할당하기 때문에, 당신이 포인터를 매크로에 유지하고 있지 말아야 하는 것은 매우 중요합니다! 그것들은 스트링을 함수 호출에 전달하기 위한 목적에서만 고안된 것입니다.

  • TCHAR_TO_ANSI(str)
  • TCHAR_TO_OEM(str)
  • ANSI_TO_TCHAR(str)
  • TCHAR_TO_UTF8(str)
  • UTF8_TO_TCHAR(str)

UnStringConv.h의 다음 헬퍼 클래스를 사용합니다:

  • typedef TStringConversion<TCHAR,ANSICHAR,FANSIToTCHAR_Convert> FANSIToTCHAR;
  • typedef TStringConversion<ANSICHAR,TCHAR,FTCHARToANSI_Convert> FTCHARToANSI;
  • typedef TStringConversion<ANSICHAR,TCHAR,FTCHARToOEM_Convert> FTCHARToOEM;
  • typedef TStringConversion<ANSICHAR,TCHAR,FTCHARToUTF8_Convert> FTCHARToUTF8;
  • typedef TStringConversion<TCHAR,ANSICHAR,FUTF8ToTCHAR_Convert> FUTF8ToTCHAR;

TCHAR_TO_ANSI 를 사용할 때 바이트의 수가 TCHAR 스트링 길이가 동일할 것이라고 가정해서는 안된다는 것은 매우 중요합니다. 멀티플 바이트 캐릭터 세트는 TCHAR 캐릭터 당 멀티플 바이트를 요구할 수 있습니다. 만약 바이트에서 스트링 길이의 결과를 알고자 한다면, 매크로 대신 헬퍼 클래스를 사용할 수 있습니다. 예를 들어:

FString String;
...
FTCHARToANSI Convert(*String);
Ar->Serialize((ANSICHAR*)Convert, Convert.Length());  // FTCHARToANSI::Length() 는 null terminator를 제외하면서 인코드 스트링에 대한 바이트의 수를 리턴합니다.

유니코드의 Non-Trivial ToUpper() 와 ToLower()


UE3 는 현재 ASCII 만 처리합니다. (ASCII | 코드 페이지 1252 | | 서유럽)

모든 언어에 대해서 이 작업을 하기에 최악인 방법은 여기에 언급된 것으로 보입니다: http://en.wikipedia.org/wiki/ISO/IEC_8859

  • ISO/IEC 8859-1 : 영어, 프랑스어, 독일어, 이탈리아어, 포르투갈어와 양 스페인어
  • ISO/IEC 8859-2 : 폴란드어, 체코어, 헝가리어
  • ISO/IEC 8859-5 : 러시아어

ftp://ftp.unicode.org/Public/MAPPINGS/ISO8859/ 의 매핑에는 위에 나열한 언어의 변환 규칙이 포함되어 있습니다. '대문자' 와 '소문자' 정보는 바라는 결과를 내기 위해 적합한 유니코드 캐릭터로 교차 참조될 것입니다.

동아시아 인코딩에 특유한 C++ 소스 코드에 대한 설명.


UTF-8 와 디폴트 윈도우 인코딩 모두 C++ 컴파일러와 관련된, 다음과 같은 문제를 야기할 수 있습니다:

Default Windows encoding
싱글 바이트 캐릭터 코드 페이지 (e.g. CP437 미국) 를 운영하면서 C++소스 코드를 윈도우에 컴파일 할 때 주의하십시오. 소스 코드가 CP932 (일본어), CP936 (중국어 간체) 혹은 CP950 (정통 중국어) 와 같은 동아시아 더블 바이트 캐릭터 인코딩을 가지고 있는지 아닌지를 말입니다.

이 동아시아 캐릭터 인코딩 시스템은 첫 번째 바이트에선 0x81-0xFE 를, 그리고 두 번째 바이트에선 0x40-0xFE 를 사용합니다. 두 번재 바이트에서의 0x5C 의 가치는 ASCII/latin-1 에서 백슬래시(backlash)로 해석될 것이고, 그것은 C++에 있어 특별한 의미를 지닙니다. (스트링 문자 내의 Escape 시퀀스, 그리고 라인의 마지막에서 사용될 때의 라인 연속).
그러한 소스 코드를 싱글-바이트 코드 페이지 윈도우에 컴파일 할 때, 컴파일러는 동아시아 더블 바이트 캐릭터 인코딩에 대해선 신경을 쓰지 않는 바, 이것은 컴파일 에러, 혹은 EXE에 버그를 만드는 등의 더 심각한 문제를 야기할 수 있습니다.

싱글-라인 코멘트(Single-line comments):
동아시아 코멘트의 마지막에 0x5c가 있는 경우, 이것은 찾기 힘든 버그, 혹은 잃어버린 라인에 의한 에러를 야기할 수 있습니다.

    // EastAsianCharacterCommentThatContains0x5cInTheEndOfComment0x5c'\'
    important_function(); /* 이 라인은 코멘트의 부분으로서 위 라인과 연결될 것입니다 */

스트링 문자 내부(Inside a string literal):
이것은 스트링의 끊어짐 혹은 인식된 0x5c escape 시퀀스와 관련된 에러를 야기할 수 있습니다.

    printf("EastAsianCharacterThatContains0x5c'\'AndIfContains0x5cInTheEndOfString0x5c'\'");
    function();
    printf("컴파일러는, 첫 번재 라인에서부터 이어졌고 이 메시지가 C++코드임을 예상했던 스트링 문자의 마지막으로 이 라인 왼쪽의 더블 인용 마크를 인식합니다.");
0x5c 를 따르는 캐릭터가 escape 시퀀스를 구체화한다면, 컴파일러는 escape 시퀀스 캐릭터 세트를 싱글 구체화 캐릭터로 변환합니다.
(구체화하지 않는다면, 결과는 설정된 것을 수행한 것이지만, MSVC 는 0x5c를 제거하고, "인지되지 않은 캐릭터 escape 시퀀스" 라고 경고합니다.)
위의 경우에서, 스트링의 끝은 0x5c 백슬래시를 가지고 있고 다음 캐릭터는 더블 인용(double quote) 이라서, escape 시퀀스 \" 는 스트링 데이터에서 더블 인용으로 변환되고 컴파일러는 다음 더블 인용 혹은 파일이 끝나기 전에 스트링 데이터를 만드는 것을 계속하여 에러를 야기합니다.

위험한 캐릭터의 예:
CP932 (일본어 Shift-JIS) "表" 은 0x955C 이고, 많은 CP932 캐릭터가 0x5C 를 가지고 있습니다.
CP936 (중국어 간체 GBK) "乗" 은 0x815C 이고, 많은 CP936 캐릭터가 0x5C 를 가지고 있습니다.
CP950 (정통 중국어 Big5) "功" 은 0xA55C이고, 많은 CP950 캐릭터가 0x5C를 가지고 있습니다.
CP949 (한국어, EUC-KR) 은 괜찮은데, 이는 EUC-KR 이 두 번째 바이트에서 0x5C 를 사용하지 않기 때문입니다.

BOM 없는 UTF-8 (몇몇의 텍스트 에디터는 BOM을 시그니쳐(signature)으로 기술합니다)
C++ 소스 코드를 동아시아 코드 페이지 CP949 (한국어), CP932 (일본어), CP936 (중국어 간체) 혹은 CP950 (정통 중국어) 윈도우에 컴파일 하는 것을 주의하십시오. 그러한 소스 코드가 UTF-8로 저장된 동아시아 캐릭터를 가지고 있는지 아닌지에 대해서 말입니다.

UTF-8 캐릭터 인코딩은 동아시아 캐릭터에 대해 세 개의 바이트를 사용합니다: 첫 번째 바이트에 대해선0xE0-0xEF, 두 번째 바이트에 대해선 0x80-0xBF 그리고 세 번째 바이트에 대해선 0x80-0xBF. BOM 없이, 동아시아 윈도우의 디폴트 인코딩은 세 개의 UTF-8 인코드 된 바이트와 다음 바이트를 두 개의 2바이트 동아시아 인코드 캐릭터로, 첫 번째와 두 번째 바이트의 한 쌍을 하나의 첫번째 동아시아 캐릭터로, 그리고 세 번째 바이트와 다음 바이트를 두 번째 동아시아 캐릭터 형성을 위한 한 쌍으로 인식합니다.
UTF-8 인코드 된 세 개의 바이트 다음의 캐릭터가 특수한 의미를 스트링 문자 혹은 코맨트에 가지고 있는 경우 문제가 야기될 수 있습니다.

인-라인 코멘트(in-line comment)에서의 예:
코멘트 텍스트가 홀수의 동아시아 문자를 가지고 있고 다음 캐릭터가 코멘트의 마지막을 표시하는 경우, 찾기 힘든 버그 혹은 잃어버린 코드로 야기되는 에러를 야기합니다.

    /*OddNumberOfEastAsianCharacterComment*/
    important_function();
    /*normal comment*/
동아시아 코드 페이지 윈도우에의 컴파일러는 디코드 된 동아시아 캐릭터 코멘트의 마지막 바이트와 별표 '*' 를 싱글 동아시아 캐릭터로 인식하여, 다음 캐릭터를 여전히 코멘트의 부분으로 취급합니다. 위의 경우에서, 컴파일러는 important_function() 을 제거하는데 이는 그것이 코멘트의 부분과도 같이 보이기 때문입니다.
이 행동은 매우 위험하고 잃어버린 코드를 찾는 것은 매우 어렵습니다.

싱글-라인 코멘트:
동아시아 코멘트의 마지막에 백슬래시 '\' 를 사용하는 것은 찾기 힘든 버그 혹은 라인을 잃어버리지 않고도 에러를 야기할 수 있습니다.

    // OddNumberOfEastAsianCharacterComment\
    description(); /* 위 라인의 마지막에 백슬래시를 사용함으로써 코더(coder)는 이 라인을 코멘트로 의도하였습니다 */
이것은 매우 드문 경우인데, 이는 프로그래머는 코멘트의 마지막에 의도적으로 백슬래시 '\' 를 써서는 안되기 때문입니다.

스트링 문자 내부:
홀수의 UTF-8 인코드 된 동아시아 캐릭터가 스트링 문자 내부에 있고 다음 캐릭터가 특수한 의미를 지니는 경우, 이것은 스트링의 끊김, 에러 혹은 경고를 야기합니다.

    printf("OddNumberOfEastAsiaCharacterString");
    printf("OddNumberOfEastAsiaCharacterString%d",0);
    printf("OddNumberOfEastAsiaCharacterString\n");
동아시아 코드 페이지 윈도우에의 C++ 컴파일러는 UTF-8 디코드 된 동아시아 캐릭터 스트링과 다음 캐릭터를 단일한 동아시아 캐릭터로 해석합니다. 당신이 운이 좋다면, 컴파일러 경고 "C4819" (비활성화가 아니라면) 혹은 에러가 당신에게 문제를 환기시킬 것입니다. 운이 나쁘다면, 스트링은 끊어질 것입니다.

결론
C++ 소스 코드를 위해 UTF-8 혹은 디폴트 윈도우 인코딩을 사용할 수 있으나, 이 문제들에 대해 주의하시길 바랍니다. 다시 한번 말하지만, 우리는 C++ 내부의 스트링 문자 혹은 UnrealScript 소스를 추천하지 않습니다. C++ 소스 코드에서 동아시아 캐릭터 인코딩을 써야만 할 경우 동아시아 언어를 당신의 디폴트 코드 페이지로서 사용하는 것을 염두에 두십시오.
또 다른 방법은 UTF-8을 BOM과 함께 사용하는 것입니다 (몇몇의 텍스트 에디터는 BOM을 유니코드 시그니쳐로 기술합니다).

노트
우리는 2010년 2월 18일에 UTF-6과 UTF-8 과 함께 몇 개의 컴파일러를 테스트하였습니다.

PC 와 Xbox360을 위한 MSVC, 그리고 PS3를 위한 gcc 혹은 slc 는 UTF-8 인코드 된 소스 코드를 컴파일 할 수 있습니다 (BOM의 유무에 상관없이). 하지만 UTF-16 (little-endian/big-endian) 은 MSVC 만 지원합니다.

퍼포스는 UTF-16 과 UTF-8 둘 모두와 함께 작동할 수 있으나, p4 diff 는 BOM 을 UTF-8 파일에서 보이는(visible) 캐릭터로 디스플레이 합니다.

외부 출처: 윈도우가 지원하는 코드 페이지

추가 참고