sitelink1 http://blog.naver.com/ruinarts/220620359206 
sitelink2  
sitelink3  
sitelink4  
sitelink5  
extra_vars6  

Chapter 02 - 아스키코드 vs 유니코드

 

Windows 프로그래밍에 있어서 GUI 프로그래밍을 하건, 시스템 프로그래밍을 하건 유니코드에 대한 이해는 필수이다



 

Section 1 - Windows에서의 유니코드(UNICODE)


 

문자셋(Character Sets)의 종류와 특성

 

가장 대표적인 문자셋에 해당하는 아스키코드와 유니코드에 대해서 간단히 언급하고자 한다.

 

아스키코드(ASCII code)는 미국에서 정의하고 있는 표준이다. 알파벳의 개수는 26개이다. 여기다가 몇몇 확장 문자를 포함해서 총 256개를 넘지 않는다. 그래서 1바이트를 가지고도 충분히 표현할 수 있다. 다시 말해서 아스키코드는 1바이트로 표현이 된다.

 

문제는 영어가 아닌 다른 국가에서 사용하는 문자들을 표현하는 일이다.

 

그래서 등장한 것이 유니코드(UNICODE)이다. 유니코드는 문자를 표현하는 데 있어서 균일하게 2바이트를 사용한다. 2바이트면 나타낼 수 있는 문자의 종류가 65,536개에 이른다. 이 정도면, 영어와 한글은 물론이거니와 전 세계의 모든 문자와 다양한 종류의 기호를 표현할 수 있다.


 

문자 셋은 그 종류에 따라서 다음과 같이 크게 세 가지 형태로 나뉘어진다.

 

[SBCS(Single byte Character Set)]

 

문자를 표현하는 데 있어서 1바이트만을 사용하는 방식이다. 아스키코드가 가장 대표적인 SBCS에 해당한다.


 

[MBCS(Multi Byte Character Set)]

 

문자를 표현하는데 있어서 동일한 바이트 수를 적용하는 것이 아니라 다양한 바이트 수를 사용해서 문자를 표현하는 방식이다.

 

유니코드는 MBCS에 속하지 않는다. 왜냐하면 MBCS는 SBCS를 포함하기 때문이다. 아스키코드에서 정의하고 있는 문자를 표현할 때에는 1바이트로 처리하고, 아스키코드에서 정의하지 않는 다른 문자를 표현할 때에만 2바이트로 처리하는 방식이다.


 

[WBCS(Wide Byte Character Set)]

 

유니코드가 WBCS 방식에 해당한다. WBCS는 모든 문자를 2바이트로 처리하는 문자셋이다.



 

MBCS의 문제점은 한글을 2byte로 처리하는 경우 strlen과 같은 함수에서 한 글자당 길이를 2로 처리한다는 것이다. 이러한 문제점의 해결책이 될 수 있는 것이 바로 WBCS 방식이다.



 

WBCS 기반의 프로그래밍

 

[char를 대신하는 wchar_t]

 

제일 먼저 문자를 표현하는 데 사용되는 자료형 char를 대신해서 자료형 wchar_t를 사용해야 한다. char형 변수는 1바이트 메모리 공간만 할당되지만, wchar_t형 변수는 2바이트 메모리 공간이 할당된다. 따라서 유니코드를 기반으로 문자를 표현하는 것이 가능하다. wchar_t는 다음과 같은 형태로 선언되어 있는 자료형이다.

 

typedef unsigned short wchar_t;


 

[“ABC”를 대신하는 L “ABC”]

 

다음은 자료형 wchar_t를 이용한 문자열의 선언을 보여준다.

 

wchar_t str[] = “ABC”;

 

만약에 이러한 형태로 문자열을 선언한다면 문제가 발생한다. 왜냐하면 배열 str은 유니코드 문자열을 저장할 준비가 되어 있음에도 불구하고, 대입 연산자 오른쪽에 존재하는 문자열은 여전히 MBCS 기반 문자열이기 때문이다. 그러므로 이 문장을 다음과 같은 형태로 변경해야 한다.

 

wchar_t str[] = L”ABC”;

 

문자열 앞에 선언된 문자 L은 “이어서 등장하는 문자열은 유니코드 기반(WBCS 기반)으로 표현하라” 라는 의미를 지난다. 따라서 이 경우에는 문자열 “ABC”는 널문자를 포함해서 총 8바이트로 표현된다. 유니코드에서는 문자열의 끝을 의미하는 널 문자까지도 2바이트로 처리된다.


 

[strlen을 대신하는 wcslen]

 

지금까지 공부했던 문자열 조작 함수들은 SBCS 기반 문자열을 처리하기 위한 함수였다. 다음 표는 SBCS 기반 문자열 조작 함수와, 이에 대응하는 WBCS 기반 문자열 조작 함수를 정리해서 보여준다.

 



 

완전한 유니코드 기반으로 : 첫 번째

 

Windows 2000 이상의 운영체제는 기본적으로 유니코드를 지원한다. 유니코드를 지원할 뿐만 아니라, 내부적으로 모든 문자열을 유니코드 기반으로 처리한다.

 

printf 함수도 문자열 처리 함수이므로, SBCS기반 문자열을 처리하는 함수이다.

 

Windows 2000 이상의 운영체제는 모든 문자열을 유니코드 기반으로 처리하기 때문에 printf는 변환에 의한 성능 문제가 있을 수 있다. 물론 유니코드 기반으로 프로그램을 작성하였다면, 성능에는 전혀 영향을 미치지 않는다. 다음 문장처럼 말이다.

 

wprintf( L “Hello world!”);

 

다음 표는 문자열 입, 출력에 관련된 일부 함수의 유니코드 버전을 보여준다.

 


 

wprintf, fputws와 같은 함수들을 통해서 유니코드 기반으로 한글을 출력하고 싶다면, 다음과 같은 함수 호출 문장이 들어가야 한다.

 

_wsetlocale (LC_ALL, L”korean”); //#include “locale.h”



 

완전한 유니코드 기반으로 : 두 번째

 

#include <stdio.h>

 

int main(int argc, char* argv[])

{

int i;

for(i = 0 ; i <argc; i++)

fputws(argv[i], stdout);

 

return 0;

}

 

main 함수의 두 번째 매개변수가 SBCS 기반으로 문자열을 전달 받기 때문에 fputws 함수 호출은 문제가 되어 컴파일 에러가 발생한다.

 

wchar_t* argv[]

 

이렇게 될 경우 컴파일 및 실행하는 과정에서는 문제가 발생하지 않는다. 다만 전혀 엉뚱한 출력결과를 보일 뿐이다. 왜냐하면 main이라는 이름의 함수는 프로그램 실행 시, 전달되는 문자열을 MBCS 기반으로 구성하기 때문이다.

 

이제 프로그램 실행 시 전달되는 문자열을 유니코드 기반으로 구성하기 위한 main 함수가 무엇인지에 관심을 두어야 한다.


 





 

Section 2 - MBCS와 WBCS의 동시 지원


 

프로그램 구현에 있어서 MBCS 기반으로 할 것이냐, 아니면 WBCS 기반으로 할 것이냐를 결정짓는 일은 골치 아픈 일이 아닐 수 없다. 물론 WBCS 기반으로 구현하는 것이 여러모로 좋을 듯하나, 아직까지 현존하는 시스템 모두가 완벽히 유니코드 기반을 지원하는 것이 아니기 때문에 문제가 발생할 소지가 높다.

 

프로그램을 한번만 구현하고, 별다른 변경 없이 MBCS 기반으로 돌아가는 형태로도, WBCS 기반으로 돌아가는 형태로도 컴파일 가능하다면 아주 만족스러울 것이다.



 

#include <windows.h>

 

windows.h는 Windows 기반 프로그래밍을 하는 데 있어서 기본적으로 항상 포함해야 하는 헤더 파일이다.



 

Windows에서 정의하고 있는 자료형

 

Windows에서는 typedef 키워드를 통하여 몇몇 기본 자료형에 Windows 스타일의 새로운 이름을 정의하고 있다.

 

typedef char CHAR;

typedef wchar_t WCHAR;


 

다음은 문자열의 주소값을 저장할 수 있는 Windows 스타일의 자료형이 어떻게 정의되어 있는지를 보여준다.

 

#define CONST const

 

typedef CHAR * LPSTR;

typedef CONST CHAR * LPCSTR;

 

typedef WCHAR * LPWSTR;

typdef CONST WCHAR* LPCWSTR;




 

Windows 스타일 자료형을 정의하는 이유

 

첫 번째는 선언의 편리성이다. typdef는 복잡한 선언을 단순하게 해준다. 다음과 같은 형태로 정의되어 있는 size_t라는 이름의 자료형이 있다.

 

typedef unsigned int size_t

 

이와 같은 정의를 통해서 unsigned int라는 긴 문장을 대신해서 간결하고, 또 의미도 보다 강하게 부여된 이름을 변수 선언 시 사용할 수 있다.


 

두 번째는 확장의 용이성이다.




 

예제 - WinString.cpp

 

#include <stdio.h>

#include<windows.h>

 

int wmain(int argc, wchar_t* argv[])

{

LPSTR str1 = “SBCS Style String 1”;

LPWSTR str2 = L”WBCS Style String 1”;

 

CHAR arr1[] = “SBCS Style String 2”;

WCHAR arr2[] = L”WBCS Style String 2”;

 

LPCSTR cStr = arr1;

LPCWSTR cStr2 = arr2;

 

printf(“%s\n”, str1);

printf(“%s\n”, arr1);

 

wprintf(L”%s\n”, str2);

wprintf(L”%s\n”, arr2);

 

return 0;

}




 

MBCS와 WBCS(유니코드)를 동시에 지원하기 위한 매크로

 

Windows에서는 MBCS와 WBCS를 동시에 수용하는 형태의 프로그램 구현을 위해서 매크로를 정의하고 있다. 다음은 Windows에 선언되어 있는 내용을 보기 좋은 구조로 간략화한 것이다.

 

#ifdef UNICODE

typedef WCHAR TCHAR;

typedef LPWSTR LPTSTR;

typedef LPCWSTR LPCTSTR;

 

#else

typedef CHAR TCHAR;

typedef LPSTR LPTSTR;

typedef LPCSTR LPCTSTR;

 

#endif


 

다음은 헤더파일 tchar.h에 선언되어 있는 내용 중 일부를 보기 좋은 구조로 편집 해놓은 것이다.

잊지 말아야 할 중요한 사실은 tchar.h는 헤더파일 windows.h에 포함되지 않는다는 것이다. 따라서 헤더파일 windows.h를 포함하더라도, 헤더파일 tchar.h는 추가로 포함시켜야 한다.

 

#ifdef UNICODE

#define _T(x) L ## x

 

#else

#define _T(x) x

#endif

 

#define _T(x) _T(x)

#define _TEXT(x) _T(x)


 

그럼 이제 위 매크로에 의해서 어떠한 일들이 벌어지는지 예를 들어 보겠다. 다음과 같이 선언된 배열이 있다.

 

TCHAR arr[10];

 

만약에 UNICODE라는 매크로가 정의되어 있지 않다면, 이 배열 선언은 전처리기에 의해서 다음과 같이 변경된다. 따라서 MBCS 타입의 문자열 저장이 가능해진다.

 

WCHAR arr[10];


 

정리해 보면 다음과 같다.

 

 

 

한가지 예를 더 제시해보면, 다음과 같이 문자열이 선언되었다.

 

_T(“HANBIT”);

 

이 매크로는 아무 조건 없이 전처리기에 의해서 다음과 같이 변경될 것이다(위의 매크로 선언을 참조하면서 따라와야 이해가 가능하다).

 

_T(“HANBIT”);

 

이 문장은 _UNICODE라는 매크로가 정의되어 있지 않으면, 다음과 같이 MBCS 타입의 문자열로 변경된다.

 

“HANBIT”;

 

반대로 _UNICODE라는 매크로가 정의되어 있다면, 전처리기에 의해서 다음과 같이 WBCS 기반의 문자열이 된다.

 

L”HANBIT”;

 

정리하면 다음과 같다.

 


 

유니코드와 MBCS를 동시에 지원하는 프로그램의 예

 

#define UNICODE

#define _UNICODE

 

#include <stdio.h>

#include <tchar.h>

#include <windows.h>

 

int wmain(void)

{

TCHAR str[] = _T("1234567");

int size = sizeof(str);

printf("string length : %d \n", size);

return 0;

}

 

//UNICODE를 주석할 경우 8

//아닐경우    16

 

MBCS 기반으로 컴파일하고자 한다면 설정창에서 _UNICODE와 UNICODE를 지우는 방법도 있지만

#undef 지시자를 통해서 이미 정의되어버린 _UNICODE를 무효화시키는 방법도 있다.



 

MBCS와 WBCS(유니코드)를 동시에 지원하기 위한 함수들

 

_UNICODE의 정의 유무에 따라서 WBCS 방식 혹은 MBCS 방식으로 컴파일을 하기 위해 printf와 같은 함수들을 모두 매크로 정의를 해야하는가?

 

물론 그래도 되지만, 이러한 수고를 덜어주기 위해서, 이미 적잘한 형태로 tchar.h에 선언되어 있으니 이를 활용하는 것이 현명한 방법이다.

 

 

tchar.h에 선언된 매크로가 어떻게 처리되는지 확인해 볼 수 있는 예제

 

#define UNICODE

#define _UNICODE

 

#include <stdio.h>

#include <tchar.h>

#include <windows.h>

 

int _tmain(int argc, TCHAR* argv[])

//main조차도 _tmain으로 선언되어 있다. 따라서 매크로

//_UNICODE의 선언에 따라 MBCS 기반의 main이 될 수도 있고,

//유니코드 기반의 wmain이 될 수도 있다.

{

LPTSTR str1 = _T("MBCS or WBCS 1");

TCHAR str2[] = _T("MBCS or WBCS 2");

TCHAR str3[100];

TCHAR str4[50];

 

LPCTSTR pStr = str1;

 

_tprintf(_T("string size : %d \n"), sizeof(str2));

_tprintf(_T("string length: %d \n"), _tcslen(pStr));

 

_fputts(_T("Input String 1 : "), stdout);

_tscanf(_T("%s"), str3);

_fputts(_T("Input String 2 : "), stdout);

_tscanf(_T("%s"), str4);

 

_tcscat(str3, str4);

_tprintf(_T("String1 + String2 : %s \n"), str3);

 

return 0;

}

 




 

이것만은 알고 갑시다


 

1. SBCS, MBCS, WBCS의 이해

 

SBCS는 문자를 표현하는 데 1바이트를, MBCS는 문자를 표현하는 데 1바이트 혹은 2바이트를, WBCS는 문자를 표현하는 데 2바이트를 사용하는 문자셋을 의미한다. 유니코드(Unicode)는 WBCS에 해당한다.


 

2. 유니코드 기반 문자열 처리 함수

 

ANSI 표준 문자열 처리 함수는 별도로 유니코드 기반을 정의하고 있다. 이들을 사용해서 유니코드 기반으로 프로그램을 작성할 수 있어야 한다.


 

3. UNICODE와 _UNICODE

 

매크로 UNICODE와 _UNICODE의 정의 유무에 따라서 함수의 선언형태가 달라진다. 이 두 매크로를 기반으로 유니코드 방식과 MBCS 방식의 컴파일이 가능하도록 프로그램을 작성할 수 있어야 한다.


 

4. 유니코드 방식과 MBCS 방식을 모두 지원하기 위한 main 함수의 구성

 

MBCS 방식을 지원하기 위해서 main이 존재한다면, 유니코드 방식을 지원하기 위해서 wmain이 존재한다. 그리고 이 두 가지 방식을 모두 지원하기 위해서 _tmain이 존재한다. 이들의 관계를 이해하고, 선언된 형태에 따라서 전달되는 문자열이 어떻게 구성되는지 확인하자.

번호 제목 글쓴이 날짜 조회 수
공지 2023 Software Development Trend 정리 황제낙엽 2024.01.19 1
88 컴퓨터와 인간의 대화[18]-베이스64 황제낙엽 2016.04.22 30
87 컴퓨터와 인간의 대화[17]-Surrogate Pair, Supplementary Characters file 황제낙엽 2016.04.22 29
86 컴퓨터와 인간의 대화[16]-UTF-16 황제낙엽 2016.04.22 15
85 컴퓨터와 인간의 대화[15]-UTF-8 황제낙엽 2016.04.22 14
84 컴퓨터와 인간의 대화[14]-euc(Extended UNIX Code) 황제낙엽 2016.04.22 15
» 컴퓨터와 인간의 대화[13]-char set 황제낙엽 2016.04.22 43
82 컴퓨터와 인간의 대화[12]-character encoding 황제낙엽 2016.04.22 10
81 컴퓨터와 인간의 대화[11]-byte 2 황제낙엽 2016.04.22 31
80 컴퓨터와 인간의 대화[10]-byte 1 황제낙엽 2016.04.22 19
79 컴퓨터와 인간의 대화[9]-EBCDIC [엡시딕] 황제낙엽 2016.04.22 73
78 컴퓨터와 인간의 대화[8]-ASCII 4 황제낙엽 2016.04.22 10
77 컴퓨터와 인간의 대화[7]-ASCII 3 황제낙엽 2016.04.22 12
76 컴퓨터와 인간의 대화[6]-ASCII 2 황제낙엽 2016.04.22 29
75 컴퓨터와 인간의 대화[5]-ASCII 1 황제낙엽 2016.04.22 6
74 컴퓨터와 인간의 대화[4]-wchar(wide character) 황제낙엽 2016.04.22 9
73 컴퓨터와 인간의 대화[3]-char 황제낙엽 2016.04.22 14
72 컴퓨터와 인간의 대화[2]-서론2 황제낙엽 2016.04.22 38
71 컴퓨터와 인간의 대화[1]-서론1 황제낙엽 2016.04.22 39
70 EULA (End User License Agreement) ; 소프트웨어 사용자 라이선스 계약 황제낙엽 2015.06.26 368
69 WYSIWYG 황제낙엽 2013.02.23 19