본문 바로가기

Major Field/Windows

문자 집합 (Character Sets)

반응형
char, wchar_t, WCHAR, TCHAR, LPWSTR, LPCTSTR, ......

-_-

아오, 뭐가 이렇게 많지..

윈도우 프로그래밍을 시작하면 다들 느끼게 되는 궁금증일 것이다.
WinNT.h 에는 아래와 같이 자료형들에 대해 define 되어 있다.

WinNT.h
..........

//
// Basics
//

#ifndef VOID
#define VOID void
typedef char CHAR;
typedef short SHORT;
typedef long LONG;
#if !defined (MIDL_PASS)
typedef int INT;
#endif
#endif

//
// UNICODE (Wide Character) types
//

#ifndef _MAC
typedef wchar_t WCHAR;    // wc,   16-bit UNICODE character
#else
// some Macintosh compilers don't define wchar_t in a convenient location, or define it as a char
typedef unsigned short WCHAR;    // wc,   16-bit UNICODE character
#endif

typedef WCHAR *PWCHAR, *LPWCH, *PWCH;
typedef CONST WCHAR *LPCWCH, *PCWCH;

typedef __nullterminated WCHAR *NWPSTR, *LPWSTR, *PWSTR;
typedef __nullterminated PWSTR *PZPWSTR;
typedef __nullterminated CONST PWSTR *PCZPWSTR;
typedef __nullterminated WCHAR UNALIGNED *LPUWSTR, *PUWSTR;
typedef __nullterminated CONST WCHAR *LPCWSTR, *PCWSTR;
typedef __nullterminated PCWSTR *PZPCWSTR;
typedef __nullterminated CONST WCHAR UNALIGNED *LPCUWSTR, *PCUWSTR;

typedef __nullnullterminated WCHAR *PZZWSTR;
typedef __nullnullterminated CONST WCHAR *PCZZWSTR;
typedef __nullnullterminated WCHAR UNALIGNED *PUZZWSTR;
typedef __nullnullterminated CONST WCHAR UNALIGNED *PCUZZWSTR;

typedef __possibly_notnullterminated WCHAR *PNZWCH;
typedef __possibly_notnullterminated CONST WCHAR *PCNZWCH;
typedef __possibly_notnullterminated WCHAR UNALIGNED *PUNZWCH;
typedef __possibly_notnullterminated CONST WCHAR UNALIGNED *PCUNZWCH;

#if _WIN32_WINNT >= 0x0600 || (defined (__cplusplus) && defined(WINDOWS_ENABLE_CPLUSPLUS))

typedef CONST WCHAR *LPCWCHAR, *PCWCHAR;
typedef CONST WCHAR UNALIGNED *LPCUWCHAR, *PCUWCHAR;

//
//  UCS (Universal Character Set) types
//

typedef unsigned long UCSCHAR;

//
//  Even pre-Unicode agreement, UCS values are always in the
//  range U+00000000 to U+7FFFFFFF, so we'll pick an obvious
//  value.

#define UCSCHAR_INVALID_CHARACTER (0xffffffff)

#define MIN_UCSCHAR (0)

//
//  We'll assume here that the ISO-10646 / Unicode agreement
//  not to assign code points after U+0010FFFF holds so that
//  we do not have to have separate "UCSCHAR" and "UNICODECHAR"
//  types.
//

#define MAX_UCSCHAR (0x0010FFFF)

typedef UCSCHAR *PUCSCHAR;
typedef const UCSCHAR *PCUCSCHAR;

typedef UCSCHAR *PUCSSTR;
typedef UCSCHAR UNALIGNED *PUUCSSTR;

typedef const UCSCHAR *PCUCSSTR;
typedef const UCSCHAR UNALIGNED *PCUUCSSTR;

typedef UCSCHAR UNALIGNED *PUUCSCHAR;
typedef const UCSCHAR UNALIGNED *PCUUCSCHAR;

#endif // _WIN32_WINNT >= 0x0600


//
// ANSI (Multi-byte Character) types
//
typedef CHAR *PCHAR, *LPCH, *PCH;
typedef CONST CHAR *LPCCH, *PCCH;

typedef __nullterminated CHAR *NPSTR, *LPSTR, *PSTR;
typedef __nullterminated PSTR *PZPSTR;
typedef __nullterminated CONST PSTR *PCZPSTR;
typedef __nullterminated CONST CHAR *LPCSTR, *PCSTR;
typedef __nullterminated PCSTR *PZPCSTR;

typedef __nullnullterminated CHAR *PZZSTR;
typedef __nullnullterminated CONST CHAR *PCZZSTR;

typedef __possibly_notnullterminated CHAR *PNZCH;
typedef __possibly_notnullterminated CONST CHAR *PCNZCH;

//
// Neutral ANSI/UNICODE types and macros
//
#ifdef  UNICODE                     // r_winnt

#ifndef _TCHAR_DEFINED
typedef WCHAR TCHAR, *PTCHAR;
typedef WCHAR TBYTE , *PTBYTE ;
#define _TCHAR_DEFINED
#endif /* !_TCHAR_DEFINED */

typedef LPWCH LPTCH, PTCH;
typedef LPCWCH LPCTCH, PCTCH;
typedef LPWSTR PTSTR, LPTSTR;
typedef LPCWSTR PCTSTR, LPCTSTR;
typedef LPUWSTR PUTSTR, LPUTSTR;
typedef LPCUWSTR PCUTSTR, LPCUTSTR;
typedef LPWSTR LP;
typedef PZZWSTR PZZTSTR;
typedef PCZZWSTR PCZZTSTR;
typedef PUZZWSTR PUZZTSTR;
typedef PCUZZWSTR PCUZZTSTR;
typedef PNZWCH PNZTCH;
typedef PCNZWCH PCNZTCH;
typedef PUNZWCH PUNZTCH;
typedef PCUNZWCH PCUNZTCH;
#define __TEXT(quote) L##quote      // r_winnt

#else   /* UNICODE */                // r_winnt

#ifndef _TCHAR_DEFINED
typedef char TCHAR, *PTCHAR;
typedef unsigned char TBYTE , *PTBYTE ;
#define _TCHAR_DEFINED
#endif /* !_TCHAR_DEFINED */

typedef LPCH LPTCH, PTCH;
typedef LPCCH LPCTCH, PCTCH;
typedef LPSTR PTSTR, LPTSTR, PUTSTR, LPUTSTR;
typedef LPCSTR PCTSTR, LPCTSTR, PCUTSTR, LPCUTSTR;
typedef PZZSTR PZZTSTR, PUZZTSTR;
typedef PCZZSTR PCZZTSTR, PCUZZTSTR;
typedef PNZCH PNZTCH, PUNZTCH;
typedef PCNZCH PCNZTCH, PCUNZTCH;
#define __TEXT(quote) quote         // r_winnt

#endif /* UNICODE */                 // r_winnt

..........

봐도 머리만 아프다.

대충 조합원리를 설명하면 아래와 같다.
LP = Long Pointer
W = Wide
C = Const
T = Template
STR = Null Terminated

LP 는 Long Pointer 의 약자로 포인터 타입을 의미하고, C 는 Const 의 약자, STR 은 Null 로 끝난다는 것을 의미한다.
W 는 Wide 의 약자로 Unicode 를 처리하기 위해 기존의 자료형/함수에 붙는 접두어로 2바이트 단위로 처리된다는 것을 뜻한다.
T 는 TCHAR / _TCHAR 를 사용하여 ANSI / Unicode 에 호환되는 코드를 사용하겠다는 것인데 아래에서 자세히 설명한다.

Ex > 아래와 같은 규칙을 적용하여 위의 WinNT.h 를 다시 살펴보자.
char = 1바이트 문자
wchar_t = WCHAR = 2바이트 문자

char* = LPSTR = 1바이트 문자열
const char* = LPCSTR = char 단위의 문자열 상수
wchar_t* = WCHAR* = LPWSTR = wchar_t 단위의 Null 로 끝나는 문자열을 의미



알파벳을 사용하는 국가에서 컴퓨터가 발전하다보니 초기의 문자열 처리는 알파벳과 특수문자 등을 대상으로 하였다. 이 것들을 처리하기 위해 char 라는 1 byte 단위의 자료형을 만들고 ANSI 표준이니 뭐니 하는 ASCII 규격으로 만들어 사용하였다.
1 byte 단위의 char 자료형으로는 256 가지(2^8 = 256)의 문자밖에 다룰 수 없었기 때문에 알파벳을 사용하지 않는 국가에서는 자신들의 언어를 표현해 줄 새로운 문자코드집합이 필요하게 되었고 자체적으로 규격을 만들어 사용하기 시작했다.

한글 인코딩의 종류
KSC5601 한글 완성형 표준(한글 2,350자표현), 한국공업표준 정보처리분야( C ) 5601번 표준안
KSC5636 : ASCII 코드를 확장하여 한글을 표현하므로 ASCII와 호환되며 역슬래시 대신 원화기호를 사용한다는 점이 ASCII와 다르다
EUC-KR : Bell 연구소에서 제안한 한글표현 방식, 영문은 KSC5636, 한글은 KSC5601으로 처리. 완성형 코드라서 조합형 코드에 비해 문자표현 수에 한계가 있다
MS949(CP949) : 마이크로소프트의 윈도우 95에서 사용하기 시작한 한글 표현방식, 조합형은 아니지만 EUC-KR형식을 확장하여 더 많은 한글을 표현함

서로 다른 규격을 사용하게 되면서 기존의 규격과 호환이 되지 않거나 프로그램에 에러가 발생하는 등 크고 작은 문제들이 발생하였고 
이러한 문제들을 해결하기 위해 다양한 문자 표현 방식이 생겨나고 전 세계의 모든 문자를 표현할 수 있는 문자코드집합인 Unicode 가 만들어지게 되었다.

다양한 문자 표현 방식

SBCS (Single-Byte Character Set) : 1바이트로 영문자 한개를 표현, ASCII코드체계는 SBCS에 속한다

DBCS (Double-Byte Character Set) : 한개의 문자를 2바이트로 표현, SBCS와 호환이 안되어 MBCS가 고안됨. 각국에서 비표준적으로 사용됨

MBCS (Multi-Byte Character Set) : 한개의 문자는 1~2바이트로 표현됨, 서로다른 언어 안에 동일한 코드를 가질 수 있는 문제가 있으므로 각 언어셋을 설정해야 하는 단점이 있다, ANSI, UTF-8, UTF-16 등은 MBCS에 속한다

WBCS : 국제표준문자집합으로 유니코드를 지원하기 위한 방식. 각국 언어가 한 테이블에 정의되므로 각 언어를 표현하는 언어셋이 필요없다.
VC++ 에서는MBCS와 Unicode가 사용되며 2바이트를 넘는 문자는 지원되지 않는다


Unicode : 전 세계의 모든 문자를 표현하기 위하여 각 문자에 숫자를 연결한 테이블

Unicode 도 아래와 같이 몇 가지로 나뉘는데 일반적으로 UCS-2 를 유니코드라고 부르며, 
C/C++ 언어에서 2byte 단위의 wchar_t (Wide Character) 라는 자료형으로 표현된다.

Unicode 인코딩의 종류

UTF-8 : 영문자는 1 바이트(8비트)로, 그 외의 문자는 2~4 바이트로 표현함

UTF-16 : 모든 문자를  2~4바이트로 표현함, Windows NT 운영체제부터 적용되고 있는 방식

UTF-32 : UCS-4와 동일하나 17개 언어만 정의한다는 점에서 UCS-4의 부분집합이라고 할 수 있다

UCS-2(Universal Character Set 2(octets)) : 한 문자를 2바이트로 고정하여 표현함, 일반적으로 유니코드라고 불림

UCS-4 (Universal Character Set 4(octets)) : 한 문자를 4바이트로 고정하여 표현함, 인코딩 방법일 뿐만 아니라 문자코딩 자체임


유니코드를 이용해서 개발을 할 때에는 주의해야 할 점들이 많다. 
 1) char 대신 wchar_t 을 사용해야 하고, 
 2) 문자열 앞에 L 을 붙여 WBCS 기반 문자열이라는 것을 명시해야 하며, 
     wchar_t name[] = L"s2junn";
 3) SBCS 기반의 함수들 대신 WBCS 기반의 문자열 처리 함수들을 사용해야 한다.

SBCS 기반 함수

WBCS 기반 함수
size_t strlen (const char *s)size_t wcslen (const wchar_t *ws)
size_t strnlen (const char *s, size_t maxlen)size_t wcsnlen (const wchar_t *ws, size_tmaxlen)
void * memcpy (void *restrict to, const void *restrict from, size_t size)wchar_t * wmemcpy (wchar_t *restrict wto, const wchar_t *restruct wfrom, size_t size)
void * mempcpy (void *restrict to, const void *restrict from, size_t size)wchar_t * wmempcpy (wchar_t *restrict wto, const wchar_t *restrict wfrom, size_t size)
void * memmove (void *to, const void *from, size_t size)wchar_t * wmemmove (wchar *wto, const wchar_t *wfrom, size_t size)
void * memset (void *block, int c, size_t size)wchar_t * wmemset (wchar_t *block, wchar_twc, size_t size)
char * strcpy (char *restrict to, const char *restrict from)wchar_t * wcscpy (wchar_t *restrict wto, const wchar_t *restrict wfrom)
char * strncpy (char *restrict to, const char *restrict from, size_t size)wchar_t * wcsncpy (wchar_t *restrict wto, const wchar_t *restrict wfrom, size_t size)
char * strdup (const char *s)wchar_t * wcsdup (const wchar_t *ws)
char * stpcpy (char *restrict to, const char *restrict from)wchar_t * wcpcpy (wchar_t *restrict wto, const wchar_t *restrict wfrom)
char * stpncpy (char *restrict to, const char *restrict from, size_t size)wchar_t * wcpncpy (wchar_t *restrict wto, const wchar_t *restrict wfrom, size_t size)
char * strcat (char *restrict to, const char *restrict from)wchar_t * wcscat (wchar_t *restrict wto, const wchar_t *restrict wfrom)
char * strncat (char *restrict to, const char *restrict from, size_t size)wchar_t * wcsncat (wchar_t *restrict wto, const wchar_t *restrict wfrom, size_t size)
int memcmp (const void *a1, const void *a2, size_t size)int wmemcmp (const wchar_t *a1, const wchar_t *a2, size_t size)
int strcmp (const char *s1, const char *s2)int wcscmp (const wchar_t *ws1, const wchar_t *ws2)
int strcasecmp (const char *s1, const char *s2)int wcscasecmp (const wchar_t *ws1, const wchar_T *ws2)
int strncmp (const char *s1, const char *s2, size_t size)int wcsncmp (const wchar_t *ws1, const wchar_t *ws2, size_t size)
int strncasecmp (const char *s1, const char *s2, size_t n)(GNU only)int wcsncasecmp (const wchar_t *ws1, const wchar_t *s2, size_t n) (GNU only)
int strcoll (const char *s1, const char *s2)int wcscoll (const wchar_t *ws1, const wchar_t *ws2)
size_t strxfrm (char *restrict to, const char *restrict from, size_t size)size_t wcsxfrm (wchar_t *restrict wto, const wchar_t *wfrom, size_t size)
void * memchr (const void *block, int c, size_tsize)wchar_t * wmemchr (const wchar_t *block, wchar_t wc, size_t size)
char * strchr (const char *string, int c)wchar_t * wcschr (const wchar_t *wstring, intwc)
char * strchrnul (const char *string, int c)wchar_t * wcschrnul (const wchar_t *wstring, wchar_t wc)
char * strrchr (const char *string, int c)wchar_t * wcsrchr (const wchar_t *wstring, wchar_t c)
char * strstr (const char *haystack, const char *needle)
wchar_t * wcsstr (const wchar_t *haystack, const wchar_t *needle)
wchar_t * wcswcs (const wchar_t *haystack, const wchar_t *needle)
size_t strspn (const char *string, const char *skipset)size_t wcsspn (const wchar_t *wstring, const wchar_t *skipset)
size_t strcspn (const char *string, const char *stopset)size_t wcscspn (const wchar_t *wstring, const wchar_t *stopset)
char * strpbrk (const char *string, const char *stopset)wchar_t * wcspbrk (const wchar_t *wstring, const wchar_t *stopset)
char * strtok (char *restrict newstring, const char *restrict delimiters)wchar_t * wcstok (wchar_t *newstring, const char *delimiters)


Visual Studio 에서는 프로젝트의 문자 집합을 선택해서 사용할 수 있도록 되어있는데, 이는 종종 사용자의 혼동을 일으킬 빌미를 제공한다.
동일한 내용이지만 프로젝트의 문자 집합 속성에 따라 문제를 일으키기도 하는 모듈이 생기는 것이다.


이러한 문제를 해결하기 위해 Microsoft 에서 TCHAR / _TCHAR 타입을 만들었다.

TCHAR/_TCHAR : 마이크로소프트사에서 만든 문자열 타입. 동일한 기능

VC 에서는 컴파일 시에 지정된 옵션에 따라서 MBCS, Unicode 형식이 적용됨, 즉, 설정옵션에 따라서 wchar_t 혹은 char 형으로 변환되어 컴파일 됨

TCHAR : winnt.h에 선언되어 있으므로 <windows.h>를 포함하는 코드에서 사용할 수 있다. VC를 사용하는 대부분의 환경에서 사용가능

_TCHAR : tchar.h에 선언되어 있다. VC가 아닌 컴파일러에서 윈도우 프로그래밍을 할 때 사용

TCHAR는 Windows SDK에 포함되어 있고 _TCHAR은 CRT에 포함되어 있다

TCHAR 자료형을 사용하면 MBCS, Unicode 방식을 가리지 않고 동일한 방법으로 코드를 작성할 수 있는 장점을 가지게 된다


마이크로소프트에서는 TCHAR 자료형을 사용하더라도 ANSI 표준 자료형으로 정해진 것은 아니다. 그러므로 VC가 아닌 다른 환경에서는 개발자가 변수이름으로 TCHAR 이라는 변수를 선언하는 실수를 범할 가능성도 있으므로 좀더 입력이 까다로운 _TCHAR 이라는 키워드를 사용하게 된것이다.

ANSI 규정에 따라 특정 컴파일러에 특화된 자료형 앞에는 '_' 문자를 붙이게 된 것이다



사실 TCHAR / _TCHAR 는 자료형이라기보다는 매크로에 가까운데 UNICODE / _UNICODE 가 정의되어 있는지에 따라 char 혹은 wchar_t 으로 변환하여 해석해준다.

typedef char    CHAR;
typedef wchar_t WCHAR;
  
#define CONST  const
typedef CHAR*   LPSTR;
typedef CONST CHAR*     LPCSTR;
typedef CONST WCHAR*    LPCWSTR;
typedef WCHAR LPWSTR;
  
#ifdef UNICODE
    typedef WCHAR   TCHAR;
    typedef LPWSTR  LPTSTR;
    typedef LPCWSTR LPCTSTR;
#else
    typedef CHAR    TCHAR;
    typedef LPSTR   LPTSTR;
    typedef LPCSTR  LPCTSTR;
#endif
  
  
#ifdef _UNICODE
    #define __T(x)      L ## x
    #define _tmain      wmain
    #define _tcslen     wcslen
    #define _tcscat     wcscat
    #define _tcscpy     wcscpy
    #define _tcsncpy    wcsncpy
    #define _tcscmp     wcscmp
    #define _tcsncmp    wcsncmp
    #define _tprintf    wprintf
    #define _tscanf     wscanf
    #define _fgetts     fgetws
    #define _fputts     fputws
#else
    #define __T(x)      x
    #define _tmain      main
    #define _tcslen     strlen
    #define _tcscat     strcat
    #define _tcscpy     strcpy
    #define _tcsncpy    strncpy
    #define _tcscmp     strcmp
    #define _tcsncmp    strncmp
    #define _tprintf    printf
    #define _tscanf     scanf
    #define _fgetts     fgets
    #define _fputts     fputs
#endif
  
#define _T(x)       __T(x)
#define _TEXT(x)    __T(x)

[출처] Win32 API 유니코드|작성자 씨피유


위와 같은 방식으로 ANSI 방식과 UNICODE, 그리고 MACRO 로 정의되는 주요한 함수들을 살펴보면 다음과 같다.

1.      Formatted I/O

MACROANSIUNICODE
_tprintfprintfwprintf
_ftprintffprintffwprintf
_stprintfsprintswprintf
_sntprintf_snprintf_snwprintf
_vtprintfvprintfvwprintf
_vftprintfvfprintfvfwprintf
_vstprintfvsprintfvswprintf
_vsntprintf_vsnprintf_vsnwprintf
_tscanfscanfwscanf
_ftscanffscanffwscanf
_stscanfsscanfswscanf
 
2.      Formatted I/O
MACROANSIUNICODE
_fgettcfgetcfgetwc
_fgettsfgetsfgetwc
_fputtcfputcfputwc
_fputtsfputsfputws
_gettcgetcgetwc
_gettchargetchargetwchar
_gettsgets_getws
_puttcputcputwc
_puttcharputcharputwchar
_puttsputs_putws
_ungettcungetcungetwc
 
3.       Stdio function
MACROANSIUNICODE
_tfopen _wfopen
_tfreopen _wfreopen
 
4.       String conversion function
MACROANSIUNICODE
_tcstodstrtodwcstod
_tcstolstrtolwcstol
_tcstoulstrtoulwcstoul
_itot_itoa_itow
_ltot_ltoa_ltow
_ultot_ultoa_ultow
_ttoiatoi_wtoi
_ttolatol_wtol
 
5.       String function
MACROANSIUNICODE
_tcscatstrcatwcscat
_tcschrstrchrwcschr
_tcscmpstrcmpwcscmp
_tcscpystrcpywcscpy
_tcscspnstrcspnwcscspn
_tcslenstrlenwcslen
_tcsclenstrlenwcslen
_tcsncatstrncatwcsncat
_tcsnccatstrncatwcsncat
_tcsnccmpstrncmpwcsncmp
_tcsncmpstrncmpwcsncmp
_tcsncpystrncpywcsncpy
_tcsnccpystrncpywcsncpy
_tcspbrkstrpbrkwcspbrk
_tcsrchrstrrchrwcsrchr
_tcsspnstrspnwcsspn
_tcsstrstrstrwcsstr
_tcstokstrtokwcstok
_tcsdup_strdup_wcsdup
_tcsicmp_stricmp_wcsicmp
_tcsncicmp_strnicmp_wcsnicmp
_tcsnicmp_strnicmp_wcsnicmp
_tcsnset_strnset_wcsnset
_tcsncset_strnset_wcsnset
_tcsrev_strrev_wcsrev
_tcsset_strset_wcsset
_tcslwr_strlwr_wcslwr
_tcsupr_strupr_wcsupr
 
6.       ctype function
MACROANSIUNICODE
_istalphaisalphaiswalpha
_istupperisupperiswupper
_istlowerisloweriswlower
_istdigitisdigitiswdigit
_istxdigitisxdigitiswxdigit
_istspaceisspaceiswspace
_istpunctispunctiswpunct
_istalnumisalnumiswalnum
_istprintisprintiswprint
_istgraphisgraphiswgraph
_istcntrliscntrliswcntrl
_istasciiisasciiiswascii
_totuppertouppertowupper
_totlowertolowertowlower

위의 비교표에서 접두어들을 비교해보면 기존에 ANSI 규격에서 str 으로 표현되던 문자열을 처리하는 함수들의 접두어가 UNICODE 에서는 wcs (Wide Character Set) 으로 MACRO 에서는 _tcs 로 변경된 것을 알 수 있다. 기존에 사용하던 코드가 새로운 프로젝트에서 형 변환 에러가 난다면 이를 참조하여 수정해보자. 

윈도우 프로그래밍을 할 때에는 가급적이면 TCHAR / _TCHAR 를 이용하는 것이 좋겠다.



참조



반응형