Win32 C/C++ 문자열 종류와 상호 변환 방법
Win32 C/C++에는 문자열을 나타내는 자료형이 다양하다. 그리고 그 자료형마다 용도와 역할이 있기 때문에 어느 하나만을 선택해서 사용할 수도 없다. 본 포스트에서는 이전 포스트에 이어서 Win32 C/C++에서 사용하는 문자열의 종류 중 Windows API에서 사용하는 문자열에 대해 설명하고 상호 변환 방법에 대해 설명하겠다.
1. CHAR, LPSTR, LPCSTR, WCHAR, LPWSTR, LPCWSTR, TCHAR, LPTSTR, LPCTSTR
지금까지의 설명의 C/C++의 표준 문자열이었다면 지금부터는 Win32에서 쓰이는 문자열 형에 대한 설명이다. 우선, Windows API에서 기본적으로 쓰이는 문자형은 CHAR과 WCHAR이다. 각각은 표준 자료형을 다르게 이름붙인 것이다.
typedef char CHAR;
typedef wchar_t WCHAR;
두 종류의 문자가 각각 NULL 문자로 끝나는 C-Style 문자열로 구성될 경우 이를 LPSTR, LPWSTR이라 한다.
typedef CHAR * LPSTR;
typedef WCHAR * LPWSTR;
LPSTR과 LPWSTR이 상수 속성을 가질 경우 LPCSTR와 LPCWSTR이라 한다.
typedef CONST CHAR * LPCSTR;
typedef CONST WCHAR * LPCWSTR;
마지막으로 Visual Studio(Visual C++) 컴파일러의 설정에 따라 유니코드를 지원하면 Widechar를 쓰고 지원하지 않으면 ANSI 문자열을 사용하도록 가변적으로 설정한 문자열이 TCHAR, LPTSTR, LPCTSTR이다. 그리고 문자열 리터럴이 이에 맞춰 지정되도록 TEXT 매크로가 사용된다.
/* TCHAR, LPTSTR, LPCTSTR */
#ifdef _UNICODE
typedef WCHAR TCHAR;
typedef LPWSTR LPTSTR;
typedef LPCWSTR LPCTSTR;
#define TEXT(str) L##str
#else
typedef CHAR TCHAR;
typedef LPSTR LPTSTR;
typedef LPCSTR LPCTSTR;
#define TEXT(str) str
#endif
1-1. CHAR과 WCHAR간 상호변환
일반적으로 Windows API는 A로 끝나는 ANSI형(예: DrawTextA)과 W로 끝나는 Widechar형(예: DrawTextW)를 모두 선언한 뒤 TCHAR 형으로 컴파일러에 따라 매크로에 의해 전환하는 형태(예: DrawText)를 갖고 있다. 하지만, 명시적으로 Widechar 문자열과 ANSI 문자열을 상호 변환하고자 한다면 다음과 같은 Windows API를 사용한다.
int MultiByteToWideChar(
IN UINT CodePage,
IN DWORD dwFlags,
IN LPCSTR lpMultiByteStr,
IN INT cbMultiByte,
OPT OUT LPWSTR lpWideCharStr,
IN INT cchWideChar
);
int WideCharToMultiByte(
IN UINT CodePage,
IN DWORD dwFlags,
IN LPCWSTR lpWideCharStr,
IN INT cchWideChar,
OPT OUT LPSTR lpMultiByteStr,
IN INT cbMultiByte,
OPT IN LPCSTR lpDefaultChar,
OPT OUT LPBOOL lpUsedDefaultChar
);
- CodePage
- Multibyte 문자열에 대해 어떤 코드페이지를 기준으로 인코드되었는지(인코드 할 것인지)를 지정하는 옵션이다.
- CP_ACP
- 현재 Windows의 기본 코드 페이지를 사용한다.
- CP_THREAD_ACP
- 본 프로그램이 실행되는 Thread에 할당된 코드 페이지를 적용한다.
- CP_UTF7 또는 CP_UTF8
- UTF-7 또는 UTF-8로 인코드한다. 이 옵션을 사용할 경우
lpDefaultChar
와lpUsedDefaultChar
매개변수는 항상NULL
이어야 한다.
- dwFlags
- 각종 플래그 옵션이다.
- WC_COMPOSITECHECK
- 강세가 붙는 문자에 대해 기본 알파벳 + 강세표시로 분리하여 인코드한다. 이 옵션을 명시하지 않을 경우 강세 붙는 문자 그대로 인코드된다.
- lpWideCharStr
- 변환할 또는 변환될 문자열을 수용하는 Widechar형 문자열 버퍼이다.
- cchWideChar
lpWideCharStr
의 버퍼의 크기이다. 단위는 문자 수이다. 상기 문자열이 NULL 문자로 끝나는 C-Style 문자열이면 이 매개변수는 -1을 전달해도 무방하다.- lpMultiByteStr
- 변환할 또는 변환될 문자열을 수용하는 Multibyte형 문자열 버퍼이다.
- cbMultiByte
lpMultiByteStr
의 버퍼의 크기이다. 단위는 바이트이다. 이 매개변수의 값으로 0을 전달할 경우 변환 시 필요한 버퍼의 바이트 수가 반환되며 상기 Multibyte형 문자열 버퍼 매개변수는 무시된다.- lpDefaultChar
- 특정 WCHAR에 대응하는 Multibyte 문자가 없는 경우(예: 유니코드에는 있는데 KS-X-1001/1002에는 없는 글자) 대체할 기본 문자이다. 단, UTF-7/UTF-8로 변환되는 경우 이 매개변수는 반드시 NULL이어야 한다.
- lpUsedDefaultChar
- 상기
lpDefaultChar
를 한번이라도 사용하게 된 경우가 생겼다면 이 매개변수로 TRUE가 출력된다. 이 매개변수를 사용할 필요가 없을 경우 NULL로 지정해도 무방하며, UTF-7/UTF-8로 변환하는 경우 이 매개변수는 반드시 NULL이어야 한다.
다음은 WCHAR형 문자열에서 CHAR형 문자열로 변환하는 예이다.
/* Example */
#include <windows.h>
/* ... */
WCHAR wcs[] = L"안녕하세요.";
CHAR mbs[56] = { '\0', };
WideCharToMultiByte(CP_ACP, 0, wcs, _countof(wcs), mbs, sizeof(mbs), NULL, NULL);
/* ... */
다음은 CHAR형 문자열에서 WCHAR형 문자열로 변환하는 예이다.
/* Example */
#include <windows.h>
/* ... */
CHAR mbs[] = "안녕하세요.";
WCHAR wcs[56] = { L'\0', };
MultiByteToWideChar(CP_ACP, 0, mbs, sizeof(mbs), wcs, _countof(wcs));
/* ... */
2. OLECHAR, LPOLESTR, LPCOLESTR
이 문자열은 ATLActive Template Library, OLEObject Linking and Embedding에서 정의된 문자열 중 하나이다. Windows 운영체제에서 이 문자열은 항상 WCHAR형 문자열과 같다. 구 Macintosh(현재의 OS X가 아닌 클래식 맥)에서 구동되던 COM/OLE에서는 이 문자열이 CHAR형 Multibyte 문자열과 같았다. 문자열 리터럴 상수를 이 문자열형에 대입하기 위해서는 OLESTR
매크로를 사용한다.
/* Definition */
#ifdef _WIN32
typedef wchar_t OLECHAR;
typedef wchar_t * LPOLESTR;
typedef const wchar_t * LPCOLESTR;
#define OLESTR(str) L##str
#else
typedef char OLECHAR;
typedef char * LPOLESTR;
typedef const char * LPCOLESTR;
#define OLESTR(str) str
#else
2-1. OLECHAR과 WCHAR 상호변환, OLECHAR과 CHAR 상호변환 OLECHAR과 TCHAR간 상호 변환
atlconv.h
헤더파일을 include하면 OLESTR, WCHAR, TCHAR, CHAR, BSTR간 상호 변환이 가능합니다. BSTR에 대해서는 다음 문단에 이어서 설명하고 여기에서는 OLESTR, WCHAR, TCHAR, CHAR간의 상호 변환만을 사용해보겠다.
OLECHAR에서 WCHAR로 변환하기 위한 함수는 다음과 같다.
- OLE2W(LPOLESTR에서 LPWSTR)
- OLE2CW(LPOLESTR에서 LPCWSTR)
- COLE2W(LPCOLESTR에서 LPWSTR)
- COLE2CW(LPCOLESTR에서 LPCWSTR)
OLESTR에서 CHAR로 변환하기 위한 함수는 다음과 같다.
- OLE2A(LPOLESTR에서 LPSTR)
- OLE2CA(LPOLESTR에서 LPCSTR)
- COLE2A(LPCOLESTR에서 LPSTR)
- COLE2CA(LPCOLESTR에서 LPCSTR)
OLESTR에서 TCHAR로 변환하기 위한 함수는 다음과 같다.
- OLE2T(LPOLESTR에서 LPTSTR)
- OLE2CT(LPOLESTR에서 LPCTSTR)
- COLE2T(LPCOLESTR에서 LPTSTR)
- COLE2CT(LPCOLESTR에서 LPCTSTR)
WCHAR에서 OLECHAR로 변환하기 위한 함수는 다음과 같다.
- W2OLE(LPWSTR에서 LPOLESTR)
- W2COLE(LPWSTR에서 LPCOLESTR)
- CW2OLE(LPCWSTR에서 LPOLESTR)
- CW2COLE(LPCWSTR에서 LPCOLESTR)
CHAR에서 OLECHAR로 변환하기 위한 함수는 다음과 같다.
- A2OLE(LPSTR에서 LPOLESTR)
- A2COLE(LPSTR에서 LPCOLESTR)
- CA2OLE(LPCSTR에서 LPOLESTR)
- CA2COLE(LPCSTR에서 LPCOLESTR)
TCHAR에서 OLECHAR로 변환하기 위한 함수는 다음과 같다.
- T2OLE(LPTSTR에서 LPOLESTR)
- T2COLE(LPTSTR에서 LPCOLESTR)
- CT2OLE(LPCTSTR에서 LPOLESTR)
- CT2COLE(LPCTSTR에서 LPCOLESTR)
위 함수를 사용하여 OLECHAR을 WCHAR, CHAR, TCHAR로 변환해보겠다. 이 함수들은 atlconv.h
에 정의되어 있으며 호출 전에 USES_CONVERSION
매크로 호출을 필요로 합니다. 이 매크로는 내부적으로 로케일에 맞춰 리셋하는 것과 관련한 내용이 있다.
/* Example */
#include <windows.h>
#include <atlconv.h>
/* ... */
OLECHAR ocs[] = OLESTR("안녕하세요.");
LPTSTR tcs = NULL;
LPWSTR wcs = NULL;
LPSTR mbs = NULL;
LPCTSTR ctcs = NULL;
LPCWSTR cwcs = NULL;
LPCSTR cmbs = NULL;
USES_CONVERSION;
tcs = OLE2T(ocs);
wcs = OLE2W(ocs);
mbs = OLE2A(ocs);
ctcs = OLE2CT(ocs);
cwcs = OLE2CW(ocs);
cmbs = OLE2CA(ocs);
/* ... */
여기서 tcs, wcs, mbs 포인터는 각 변환함수로부터 반환된 포인터를 반환받는데, 이 포인터를 직접 해제할 필요는 없다. 오히려 해제하면 안 된다. 왜냐하면 MSDN이 이 버퍼는 정적 버퍼이므로 해제하지 않는다고 되어 있기 때문이다. 다만, 정적버퍼이기 때문에 너무 1MB를 넘는 긴 문자열을 이 함수를 통해 변환해서는 안될 것이다.
이번에는 이 ATL 매크로 함수를 사용하여 TCHAR, CHAR, WCHAR간 상호 변환에 대해 알아보겠다.
TCHAR과 CHAR간 변환하기 위한 함수는 다음과 같다.
- T2A(LPTSTR에서 LPSTR)
- T2CA(LPTSTR에서 LPCSTR)
- CT2A(LPCTSTR에서 LPSTR)
- CT2CA(LPCTSTR에서 LPCSTR)
- A2T(LPSTR에서 LPSTR)
- A2CT(LPSTR에서 LPCSTR)
- CA2T(LPCSTR에서 LPSTR)
- CA2CT(LPCSTR에서 LPCSTR)
TCHAR과 WCHAR간 변환하기 위한 함수는 다음과 같다.
- T2W(LPTSTR에서 LPWSTR)
- T2CW(LPTSTR에서 LPCWSTR)
- W2T(LPWSTR에서 LPTSTR)
- W2CT(LPWSTR에서 LPCTSTR)
- CT2W(LPCTSTR에서 LPWSTR)
- CT2CW(LPCTSTR에서 LPCWSTR)
- CW2T(LPCWSTR에서 LPTSTR)
- CW2CT(LPCWSTR에서 LPCTSTR)
CHAR과 WCHAR간 변환하기 위한 함수는 다음과 같다.
- A2W(LPSTR에서 LPWSTR)
- A2CW(LPSTR에서 LPCWSTR)
- W2A(LPWSTR에서 LPSTR)
- W2CA(LPWSTR에서 LPCSTR)
- CA2W(LPCSTR에서 LPWSTR)
- CA2CW(LPCSTR에서 LPCWSTR)
- CW2A(LPCWSTR에서 LPSTR)
- CW2CA(LPCWSTR에서 LPCSTR)
3. CString
CString
은 MFCMicrosoft Foundation Class에서 사용하는 문자열로서 Multibyte String 또는 Wide String 및 각종 문자열 함수를 래핑한 자료형이다. C++ 클래스로 작성되었으며 생성자를 통해 다양한 형식의 문자열을 받아 내부적으로 자동 변환하므로 사용이 편하다.
다음은 CHAR, WCHAR, TCHAR 문자열을 각각 CString으로 변환하는 예이다.
/* Example */
#include <afxwin.h>
/* ... */
TCHAR tcs[] = TEXT("피자");
WCHAR wcs[] = L"햄버거";
CHAR mbs[] = "스파게티";
CString cst1 = CString(tcs);
CString cst2 = CString(wcs);
CString cst3 = CString(mbs);
/* ... */
CString에서 포인터 문자열로의 변환은 GetBuffer
함수로 수행한다. 이 때 반환형이 TCHAR임에 주의하기 바라며, 사용 후에는 반드시 ReleaseBuffer
를 호출한다.
/* Example */
#include <afxwin.h>
/* ... */
CString cst = CString(TEXT("안녕하세요."));
LPTSTR tcs = NULL;
tcs = cst.GetBuffer();
// ... 포인터 문자열에 대한 작업
cst.ReleaseBuffer();
/* ... */
3. 마무리
여기까지 해서 Windows API 및 ATL 문자열에 대해 설명하였다. 다음 포스트에서는 COM 문자열에 대해 설명하겠다.
- [Win32 문자열 종류와 상호 변환 방법] Part 1 - C/C++ 기본 문자열
- [Win32 문자열 종류와 상호 변환 방법] Part 2 - Windows API 문자열
- [Win32 문자열 종류와 상호 변환 방법] Part 3 - COM/.NET 문자열 (完)