2008년 6월 16일 월요일

WinCE에서의 스트링 사용

WinCE에서 스트링을 처리할 때는 기존의 표준 ANSI C 함수(strcpy, strcmp 등)를 사용할 수 없다. WinCE가 유니코드를 사용하기 때문이다. 유니코드라고 해서 특별히 복잡한 것은 없는데 다만 1바이트로 처리하던 문자(ANSI의 경우 표준 아스키코드 128종류문자, 확장 특수문자까지 포함하면 256가지)를 2바이트로 대폭 확장했다는 차이만이 있을 뿐이다. 그렇지만 이 차이로 인해 PC상에서 주로 썼던 친숙한 스티링 함수 등은 전부 쓸 수 없게 되었다. 대신 한글처리나 기타 여러 언어의 문자를 처리함에 있어서 언어버전마다 다른 복잡한 PC윈도 프로그램 보단 통일성 있게 프로그래밍이 가능해졌다.

스트링의 선언
- ANSI의 경우(컴파일러 환경에 _MBCS라는 매크로가 정의된 경우)
char string[10];

- Unicode 의 경우(컴파일러 환경에 UNICODE나 _UNICODE라는 매크로가 정의된 경우)
wchar_t string[10];

두 가지를 통합하는 변수타입도 있는데 다음과 같이 선언해주면 ANSI든 Unicode든 상관없이 컴파일러 옵션에서 알아서 처리한다.
TCHAR string[10];

앞으로는 WinCE 의 스트링을 처리할 때는 꼭 'TCHAR'를 기억하자. 참고로 TCHAR는 다음과 같이 선언되어 있다.
#ifdef _UNICODE
#define TCHAR wchar_t
#endif
#ifdef _MBCS // ANSI의 Ascii 코드 방식일 때
#define TCHAR char
#endif

스트링을 선언과 함께 초기화할 때
- ANSI의 경우
char szString[20] = "I am a girl";

- Unicode의 경우
wchar_t szString[20] = L"I am a girl";

유니코드의 경우 쌍따옴표 앞에서 붙은 'L'이라는 매크로를 붙임으로 해서 쌍따옴표 부분이 유니코드로 변환된다. 하지만 실제로 가장 많이 쓰는 변수형은 다음과 같다.

TCHAR szString[20] = _T("I am a girl");
TCHAR szString[20] = TEXT("I am a girl");

_T()와 TEXT()는 사실 같은 역할을 하는 매크로로 컴파일러 환경이 ANSI로 정의되어 있으며 괄호안에 스트링을 그대로 두어 ANSI코드로 만들고 UNICODE로 되어 있으면 'L'이라는 매크로를 앞에 붙여서 괄호안의 스트링을 유니코드로 만드는 것과 같은 역할을 한다.
즉, 다음과 같은 역할을 하는 것이다.

- ANSI의 경우
TCHAR szString[20] = _T("I am a girl");
-> char szString[20] = "I am a gril";

-Unicode의 경우
TCHAR szString[20] = _T("I am a girl");
-> wchar_t szString[20] = L"I am a girl";

결론적으로 가장 중요하게 강조하고 싶은 저은 TCHAR와 _T("")/TEXT("") 매크로로 프로그램이나 모듈을 짜놓으면 개발환경이 ANSI인지 유니코드인지 상관없이 컴파일이 가능한 코드를 작성할 수 있다. 가급적으로 WinCE에서는 이 매크로들만 사용하기를 강하게 추천하는 바이다.
또 주의해야 할 점은 TCHAR와 _T(""), TEXT("") 같은 통합 매크로를 사용했으면 지속적으로 사용해야지 중간에 char, tchar나 'L' 매크로 등을 소스 중간 중간에 써넣으면 나중에 간혹 환경이 다른 상황에서 컴파일 할 때의 자잘한 수 백 개의 컴파일 에러들이 짜증날 가능성이 높다.

스트링 처리 함수
C 프로그램을 짜다가 보면 아마 가장 많이 사용하는 함수 중의 하나가 strcpy, strlen 등의 스트링 처리 함수일 것이다. 하지만 이런 함수들은 기본적으로 1바이트 차계의 스트링들을 처리하기 위한 함수들이므로 WinCE의 유니코드 환경에서 제대로 작동하지 않는다.
- ANSI에서 스트링 처리 함수
strcpy, strncpy, strlen, strcmp, strncmp 등
- Unicode에서 스트링 처리 함수
wcscpy, wcsncpy, wcsnlen, wcscmp 등

즉 str로 시작되는 접두사 부분을 wcs로 바꾸어 프로그램을 짜면 된다. 또 TCHAR형식처럼 ANSI와 유니코드를 동시에 지원하려면 다음 함수들을 사용하면 되며, 모두 'tchar.h'에 정의되어 있다.
_tcscpy, _tcsncpy, _tcsnlen, _tcscmp 등

스트링 입출력 함수
- ANSI : printf, sprintf, scanf
- Unicode : wprintf, swprintf, wscanf
- TCHAR : _tprintf, _stprintf, _tscanf

실제 사용 예제는 다음과 같다.
{
TCHAR szTemp1[100] = _T("You are my brother.");
TCHAR szTemp2[100] = _T(" And we are the world.");
TCHAR szTemp3[100] ;

_tcscat(szTemp1, szTemp2);
_stprintf(szTemp3, _T("%s -> Merge string"), szTemp1);
MessageBox(NULL, szTemp3, _T("Test"), MB_OK);
}

Win32에서는 sprintf를 쓸 수 없고 wsprintf를 써야만 되는 경우가 있어서 위 소스가 컴파일 에러를 낼 수 도 있다. 이 경우 다음의 사용자 정의 스트링 처리 함수 부분을 참조하자.

사용자 정의 스트링 처리 함수
아무리 TCHAR 형식 스트링이 ANSI/유니코드를 동시 지원한다고는 하지만 실제 프로그래밍을 들어가 보면 사용자 정의 형식의 함수가 필요할 때가 아주 많다. 예를 들어 위 스트링 처리 함수들을 응용하고 재조합하는 함수를 만들다가 보면 중간에 별도의 다른 내용의 함수가 필요할 때가 종종 있다.
예를 들어 다음과 같이 Sample이라는 스트링의 실제 문자 수를 알고 싶다면
TCHAR Sample[10] = _T("Test");

ANSI에서는 strlen()함수, 유니코드에서는 wcslen()함수 등을 쓰면 된다. 혹은 _tcslen함수를 써도 마찬가지다. 근데 만약 알고 싶은 것이 실제 문자수가 아니라 스트링이 몇 바이트인지 알고싶을 때는 _tcslen 함수를 사용해도 알 수 없다.
왜냐하면 _tcslen함수는 무조건 4라는 리턴값을 취할 것이기 때문이다. 유니코드 상태에서 Sample이라는 변수의 "Test"란 스트링이 차지하는 바이트 수는 8이다(4개의 문자 * 유니코드 기본바이트 2). 이 경우는 비교적 간단한 경우라서

_tcslen(Sample) * sizeof(TCHAR)

이런 식으로 리턴값을 얻어내면 되겠지만 모든 소스코드 라인에 이런 귀찮은 표현을 타이핑 하기에도 아주 번거로울 것이다. 이런 경우에 대비하여 아예 my_strbyte()란 함수를 만들어두고 필요할 때마다 사용하면 편할 것이다. 다음과 같이 사용자 정의 함수를 만들면 된다.
inline int my_strbyte( TCHAR *szString )
{
#ifdef _MBCS // ANSI
return strlen(szString);
#endif
#ifdef _UNICODE // Unicode
return wcslen(szString) *2
#endif
}

그리고 어쩌다가 윈도 표준 스트링 함수와 스트링 처리 함수가 충돌하는 경우가 있는데 이런 경우(대표적 예가 _strintf 이다) 다음과 같이 define 형식으로 처리하면서 각각 상황에 맞는 함수를 정의 내릴 수 있다.
// my_stprintf의 등록
#ifdef _MBCS
#define my_stprintf wsprintf
#endif
#ifdef _UNICODE
#define my_stprintf swprintf
#endif

윗 부분을 정의한 다음에 윗 장에서 썼던 예제 소스를 다시 수정하여 넣어보면 적어도 윈도우를 사용하는 어떠한 플랫폼에서도 제대로 컴파일 되고 아무런 문제가 없이 실행됨을 알 수 있다.

다음은 예제 소스이다.
{
TCHAR szTemp1[100] = _T("You are my brother.");
TCHAR szTemp2[100] = _T(" And we are the world.");
TCHAR szTemp3[100] ;

_tcscat(szTemp1, szTemp2);
my_stprintf(szTemp3, _T("%s -> Merge string"), szTemp1);
MessageBox(NULL, szTemp3, _T("Test"), MB_OK);
}

그 외 여러 가지 사용자 정의 함수의 예들을 넣었다. 필자가 초기에 _tcs 계열의 함수들이 미덥지 못해서 만든 함수들인데 비록 모든 함수들이 _tcs(XXX)형식으로 다 있는 것이기는 하지만 예제를 보고 충분히 재활용할 수 있게 소스를 실었다.

// my_strcpy함수: UNICODE와 ANSI 코드 동시 지원
TCHAR inline *my_strcpy(TCHAR* dest, TCHAR* src)
{
#ifdef _MBCS
return strcpy(dest, src);
#endif
#ifdef _UNICODE
return wcscpy(dest, src);
#endif
}

// my_strncpy함수: UNICODE와 ANSI 코드 동시 지원
TCHAR inline *my_strncpy(TCHAR* dest, TCHAR* src, int n)
{
#ifdef _MBCS
return strncpy(dest, src, n);
#endif
#ifdef _UNICODE
return wcsncpy(dest, src, n);
#endif
}

// my_strtok함수: UNICODE와 ANSI 코드 동시 지원
TCHAR inline *my_strtok(TCHAR* buff, TCHAR* token)
{
#ifdef _MBCS
return strtok(buff, token);
#endif
#ifdef _UNICODE
return wcstok(buff, token);
#endif
}

// my_atoi함수: UNICODE와 ANSI 코드 동시 지원
int inline my_atoi(TCHAR* str)
{
#ifdef _MBCS
return atoi(str);
#endif
#ifdef _UNICODE
return _wtoi(str);
#endif
}

// my_strlen함수: UNICODE와 ANSI 코드 동시 지원
int inline my_strlen(TCHAR* str)
{
#ifdef _MBCS
return strlen(str);
#endif
#ifdef _UNICODE
return wcslen(str);
#endif
}
parent.ContentViewer.parseScript('b_388771');

댓글 없음: