물마중

[Windows System] 문자셋 (Character Sets) 본문

Programming/Windows System

[Windows System] 문자셋 (Character Sets)

zweistar2 2011. 1. 22. 15:55

1. 문자셋(Character Set)의 종류와 특성

SBCS(Single Byte Character Set) : 1바이트로 문자를 표현한다. 대표적으로 아스키 코드(ascii code)가 있다.
MBCS(Multi Byte Character Set) : 문자를 표현 하는데 있어서 아스키 코드는 1바이트로, 그 외에는 2바이트로 처리한다.
WBCS(Wide Byte Character Set) : 모든 문자를 2바이트로 표현한다. 대표적으로 유니코드(UNICODE)가 있으며, 전세계의 모든 문자를 표현할 수 있다.

2. WBCS 기반 프로그래밍

기본적으로 운영체제에서는 문자열을  MBCS 기반으로 처리한다.

예)

#include <stdio.h>
#include <string.h>

int main()
{
         char str[] = "AB가나";
         int len = strlen(str);
         printf("%d", len);

         return 0;
}

실행 결과

이 코드의 실행 결과에서 MBCS 기반으로 프로그래밍 했을때 생기는 문제점을 알게될 것이다. strlen(string length)함수는
입력한 문자열의 길이를 구하고자 하는 함수이다. 보는 바와 같이 "AB가나" 즉 4개의 문자들이 입력한 것을 알 수 있지만, strlen 함수는 널문자를 제외한 바이트 크기 즉 6바이트를 값으로 출력한다. 이는 프로그래머가 원하는 결과가 절대 아닐 것이다. (문자열마다 할당 바이트가 다르기 때문에 생기는 문제점)

이러한 문제점의 한 해결책이 WBCS 기반 프로그래밍이다.

WBCS 기반으로 프로그래밍 하기 위해선 SBCS 기반의 함수를 WBCS 기반의 함수로 바꿔줘야 한다.

 SBCS 함수  WBCS 함수
 main  wmain
 char  wchar_t
 "상수"  L"상수"
 strlen   wcslen
 strcpy  wcscpy
 strncpy  wcsncpy
 strcat  wcscat
 strncat  wcsncat
 strcmp  wcscmp
 strncmp  wcsncmp
 printf  wprintf
 scanf  wscanf
 fgets  fgetws
 fputs  fputws

(참고 : wprintf, fputws 와 같은 함수들을 통해서 유니코드 기반으로 한글을 출력하고 싶다면 함수를 호출하기 전에
_wsetlocale(LC_ALL, L"korean");        // #include <locale.h>
_wsetlocale는 프로그램이 실행되는 나라 및 지역에 대한 정보를 설정하는 함수이다.)

WBCS 함수를 사용할때는 파라미터도 모두 유니코드 기반 문자열이어야 한다.

예)

#include <stdio.h>

int wmain(int argc, wchar_t* argv[])
{
       for(int i = 1 ; i < argc; i++)
       {
                wprintf(L"%s", argv[i]);
       }
       return 0;
}


3. MBCS, WBCS 동시지원 프로그래밍
일반적인 운영체제는 MBCS를 지원하지만 이도 점점 WBSC로 넘어오고 있다. 하지만 프로그램의 호환성을 위해서 두 문자셋 환경에서 모두 올바르게 동작하기 위한 프로그래밍 습관이 필요하다. 이를 위해서 조건부 컴파일을 사용한다.

1) Windows에서 정의하고 있는 새로운 자료형 
   소주제에서 새로운 자료형이라고 표현하였지만, 전혀 새로운 자료형이 아니다. 기존에 존재하는 자료형을 조합하여 windows 만의 자료형으로 새롭게 정의한 것에 지나지 않는다. 이는 의미를 더욱 확실하게 하며, 코드 수정 시에 발생하는
많은 시간의 소비와 오류발생률을 줄일 수 있다.
(프로젝트 진행 시에 그 성격에 맞는 자료형을 정의하여 사용하면 많은 장점을 갖는다. 예를 들어 사이즈를 표시하는 자료형을 단순히 int형이나 long형등의 정수 자료형을 쓰는 것보다 새롭게 정의하여 size_t 등으로 사용하는 것이 더욱 효율적이다.)

typedef      char                      CHAR;
typedef      wchar_t                 WCHAR;
 
#define      CONST                  const;

typedef     CHAR*                    LPSTR;
typedef     CONST CHAR*        LPCSTR;

typedef     WCHAR*                 LPWSTR;
typedef     CONST WCHAR*     LPCWSTR;


위에서 언급했던 내용을 다시 정리하겠다.

프로젝트 진행시에 자료형을 정의하여 사용하면 좋은 이유
1. 선언의 편리성 : typedef unsigned int size_t (긴 문장을 간결하게 대체하며 의미도 강하게 부여된다)
2. 확장의 용이성 : typedef unsigned char STR_LEN -> typedef unsigned int STR_LEN
(한번의 수정으로 STR_LEN 자료형을 쓰는 모든 변수가 char->int 로 확장되었다))


2) MBCS와 WBCS를 동시에 지원하기 위한 매크로

Windows에서 MBCS와 WBCS를 동시에 수용하는 형태의 프로그램 구현을 위해서 매크로를 정의하고 있다.

#ifdef   UNICODE
     typedef WCHAR         TCHAR;
     typedef LPWSTR        LPTSTR;
     typedef LPCWSTR      LPCTSTR;
#else
     typedef CHAR           TCHAR;
     typedef LPSTR          LPTSTR;
     typedef LPCSTR        LPCTSTR;
#endif

위의 조건부 컴파일 구문을 잠시 설명하면 처음 시스템환경을 조사하여 UNICODE기반의 환경이라면(#indef UNICODE) TCHAR, LPTSTR, LPCTSTR 등을 WCHAR, LPWSTR 등의 유니코드 자료형으로 사용하지만, 그렇지 않다면(#else) MBSC환경의 자료형으로 정의된다.


#ifdef   _UNICODE
     #define  __T(x)  L  ##  x
#else
     #define  __T(x)  x
#endif

#define  _T(x)        __T(x)
#define  _TEXT(x)  __T(x)


3) MBCS와 WBCS를 동시에 지원하기 위한 함수

tchar.h 에 선언되어 있는 함수이름 매크로이다.

#ifdef _UNICODE 
  #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 _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

 4) 실제 적용( 위에 1), 2), 3)의 선언들을 잘 참조하면서 하나씩 순서대로 적용해본다)

TCHAR str[5];

#define UNICODE (유니코드가 정의되어 있다면)
TCHAR str[5];  ->  WCHAR str[5];  ->  wchar_t str[5];

유니코드가 정의되어 있지 않다면
TCHAR str[5];  ->  CHAR str[5];    ->  char str[5];

_T("BAB5");

_T("BAB5");  -->  __T("BAB5");

#define _UNICODE
__T("BAB5");  -->  L"BAB5";

유니코드가 정의되어 있지 않다면
__T("BAB5");  -->  "BAB5";

_tmain

#define _UNICODE
_tmain  -->  wmain

유니코드가 정의되어 있지 않다면
_tmain  -->  main


예)

#define UNICODE       // WBCS 지원, 만약 이부분을 주석처리할 경우 MBCS 지원
#define _UNICODE     // WBCS 지원, 만약 이부분을 주석처리할 경우 MBCS 지원

#include <stdio.h>
#include <windows.h>
#include <tchar.h>

int _tmain(int argc, TCHAR* argv[])
{
        LPTSTR str1 = _T("HACKER");
        TCHAR str2[100] = _T("BAB5");
        TCHAR str3[20];        

        LPCTSTR pstr = str1;
        
        _tprintf(_T("str1 len = %d \n"), _tcslen(str1));
        _tscanf(_T("%s"), str3);
        
        _tcscat(str2, str3);
        _tprintf(_T("%s\n"), str2);
 
        return 0;
}