C++의 다양한 string 타입 | C & C++

Program C/C++ 2010. 1. 30. 21:14 Posted by HisPark
C++의 다양한 string 타입 | C & C++

  
http://cafe.naver.com/newchany/623  



윈도우에서 C++로 코딩을 하다보면(물론 unix 에서도) String처리에 관련된 매우 다양한 타입의 데이타들을 보실 수 있습니다. 헷갈리는 여러 스트링 타입들을 정리해 보도록 합시다. 특정 스트링 타입과 제공되는 API를 사용할 때 생각해 봐야할 체크포인트는 세가지 정도 될 것 같습니다.

1. 인코딩 문제
2. 버퍼 오버 플로우 문제
3. 쓰레드 안전성 문제


기본부터 - char
언제나 시작은 기본부터. char부터 시작해 보도록 하겠습니다. 8bit를 담을 수 있는 자료형입니다. 안에 무얼 담아도 상관없지만 8비트 단위기 때문에 ASCII나, 각 인코딩 별 MBCS, UTF-8도 이곳에 담을 수 있습니다. C에서 지원하는 char 를 위한 api는 아래와 같은 것들이 있습니다.

구분 대상
길이 파악 strlen
문자열 복사 strcpy, strncpy, strcat, strncat
문자열 비교및 검색 관련 strcmp, strncmp, strchar, strstr,
파싱 관련 atoi, atof, atol, itoa
입 출력 함수 printf, sprintf, sscanf

토크나이징 관련 strtok

char 계열의 함수들을 사용할 때의 주의 사항들입니다. 사실 다국어 버전을 사용한다면 이 보다는 wchar_t를 사용하는 것이 좋습니다. MBCS를 이용해서 작성된 코드는 타 언어의 OS환경에서 수행하면 모든 글자들이 깨져서 보입니다.

strlen() 먼저 보겠습니다. 이 녀석은 단지 버퍼안에 0이 나타날때까지의 길이를 리턴해 주는 함수 입니다. 만약 MBCS 한글이 있다면 한글을 2글자로 처리할 것입니다. 해서 총 글자 수 보다 많은 값이 리턴 될 것입니다. 아래에서 더 살펴보도록 하겠습니다.

strcpy() 는 사용하지 않는 것이 좋습니다. 공간이 적은 목적지에 큰 데이타를 넣을라 치면 버퍼가 오버플로우 되서 문제가 발생합니다. 이보다는 strncpy() 를 사용하셔야 합니다.

strcat() 역시 strcpy()와 같은 이유로 strncat()을 사용하셔야 합니다. 한가지 더 고려해야할 부분이 있는데 strncat()은 뒤에 NULL문자를 붙여주지 않기 때문에 스트링의 끝을 찾을 수 없는 문제가 발생합니다.

strtok() 함수는 내부에 static변수를 사용합니다. 즉 쓰레드에 안전하지 않습니다.



유니코드 - wchar_t
wchar_t는 윈도우 환경에서 유니코드를 쓸 수 있도록 만든 데이타 형입니다. 크기가 컴파일러 별로 통일되지 않은 데이타 타입인데 자세한 내용은 문자열 인코딩 글에서 확인하실 수 있습니다. 어찌 되었건 윈도우 환경에서 wchar_t는 16비트이고 char를 위한 api와 비슷한 api를 아래와 같이 제공합니다.

구분 대상
길이 파악 wcslen
문자열 복사 wcscpy, wcsncpy, wcscat, wcsncat
문자열 비교및 검색 관련 wcscmp, wcsncmp, wcschar, wcsstr,
파싱 관련 _wtoi, _wtof, _wtol, _itow
입 출력 함수 wprintf, wsprintf, wsscanf

토크나이징 관련 wcstok


목록을 보면 str 부분이 모두 wcs 로 바뀌어 있습니다. atoi 와 같은 함수는 _wtoi로 바뀌었네요. 하여간 이함수들 역시 위에서 본 char용 api가 가지는 문제들을 그대로 가지고 있기 때문에 똑같이 주의해서 사용하셔야 합니다.

wcslen() 함수 하나만 살펴보도록 하겠습니다. 이 함수는 strlen()보다는 보다 똑똑한 출력값을 리턴합니다. 하지만 그렇다고 안심할 수는 없습니다. 코드를 보고 설명드리도록 하겠습니다.


char str1[] = "abc가나다";  
wchar_t str2[] = L"abc가나다";  


printf("char 문자열 길이 : %d \n", strlen(str1) );  
printf("wchar_t 문자열 길이 : %d \n", wcslen(str1) );  


/*
아웃풋은 다음과 같습니다.


char 문자열 길이 : 9
wchar_t 문자열 길이 : 6
*/


wchar_t는 한글이고 영문이고 모두 16비트 공간을 할당하기 때문에(윈도우 환경에선) 실제 길이에 맞는 글자 수가 리턴됩니다. 하지만 UNICODE는 16비트 BMP영역 밖의 문자도 있습니다. 일부 특수문자와 확장 한문이 그렇죠. UTF-16은 BMP영역 밖의 문자열에 대해서는 두 wchar_t를 합쳐 하나의 하나의 뜻을 내도록 합니다. 따라서 wcslen이 정말 글자 갯수를 리턴한다는 보장이 없습니다. 반면 gcc에서는 wchar_t가 32비트 할당이 됩니다. 이경우에는 wchar_t두개를 합치거나 할 필요가 없습니다. 이 때는 확실히 wcslen()이 글자 수를 리턴한다고 할 수 있습니다.

한가지 더 "L"에 대하 이야기 해보도록 하겠습니다. wchar_t str2[] = L"abc가나다" 바로 이부분인데요. 이 글을 보시는 많은 분들이 저 L이 다음에 나오는 스트링이 유니코드라는 것을 표시할 때 쓴다는 것을 알고 계실 겁니다. 그렇습니다. 다시 이야기하면 L은 컴파일러에거 뒤에나오는 스트링은 wchar_t 에맞는 UTF-16(윈도우의 경우)이거나 UTF-32(gcc의 경우)라는 것을 알려 줍니다.

하지만 여기서 짚고 넘어가야 할 것이 하나 있습니다. 실제 "abc가나다" 는 소스코드에 적힌 텍스트일 이 글자는 뭘로 인코딩 되어 있을까요!? 갑자기 멍~ 해지는 군요. 사실 이 텍스트는 시스템에 따라 달라집니다. 한글 윈도우의 경우에는 CP949형식으로 인코딩 되어 있습니다. 중국어나 일본어가 설치된 윈도우라면 다른 인코딩 방식으로 적혀 있을 테죠. 즉 컴파일러는 L이 나오면 뒤에 나오는 문자열을 UNICODE로 컨버팅 합니다. 한글 윈도우라면 CP949에서 UNICODE로 변환하겠죠. 한글로된 소스코드를 일본어가 깔린 컴퓨터에서 컴파일 한다면 어떻게 될까요? L""뒤에 적힌 한글을 제대로 변환해 줄까요? 컴파일러는 L""뒤에 있는 문자열이 SHIFT_JIS라고 생각할 것입니다.(CP932 나 EUC-JP 일지도;;) 당연히 엄하게 컨버팅 할테죠. 이런 현상을 막기 위해서는 아래와 같이 해주시면됩니다.


// 아래 함수를 이용하는 방법
setlocale(LC_ALL, ".949");


// VC의 경우 #pragma를 이용하는 방법
#pragma setlocale(".949")




TCHAR와 기타 매크로
간단히 char 와 wchar_t를 정리해봤습니다. 만약 유니코드로 작성된 코드를 MBCS로 고치고 싶다면 어떨까요? 또는 반대라면. 일일이 다 L을 붙이고 함수이름을 다 바꾸고 얼마나 귀찮겠습니까. 그래서 TCHAR라는 매크로가 있는데요. TCHAR는 프로젝트에 UNICODE사용이 설정되어 있으면 wchar_t 아니면 char로 정의 됩니다. 위에서 말한 모든 함수드로 _tcs 로 시작하는 함수를 사용하는 방법으로 대치 할 수 있습니다. 또 L 키워드 대신에 _T("문자열") 방법을 사용하시면 되구요. 이글에 관심을 가지시는 분들은 대부분 아시리라 생각 됩니다.

몇몇 매크로도 정리해보겠습니다. 아래는 윈도우에만 해당하는 이야기 입니다. 각 매크로를 따라가면 몇번에 걸처 typedef 되어 있긴 하지만 끝까지 따라가면 결국 아래와 같습니다.
매크로 뜻
LPSTR
LPCSTR
char *
const char *

LPWSTR
LPCWSTR
wchar_t *
const wchar_t *

LPTSTR
LPCTSTR
TCHAR *
const TCHAR *

LP는 포인터, C는 const, W는 wchar_t, T는 TCHAR 라고 생각하면 기억하기 쉽습니다.



string과 wstring
이 두놈은 STL 표준에 있는 스트링 처리 클래스(일종의,,)입니다. string은 char를 담을 수 있고, 이름에서도 알 수 있듯이 wstring은 wchar_t를 담을 수 있습니다. 다들 알고 계시겠지만 몇가지만 특징만 적어보자면..

스트링 길이에 맞게 크기가 알아서 조절 됩니다.
버퍼 사이즈와 이에 따르는 버퍼 오버플로우 문제가 해결됩니다.

+= 연산자를 지원합니다. strcat()에 의한 오버 플로우 문제가 없습니다.
기존 CAPI에서 사용할 수 있습니다. 단 읽기 전용으로만 가능합니다.

표준입니다! 어느 플랫폼에서도 컴파일 가능하고 올바로 동작합니다.
다양한 기능들에 대해서는 웹을 더 참고하시기 바랍니다. 여기서는 이정도만 다루고 인코딩 컨버팅 때에 좀더 이야기 해보도록 하겠습니다. STL의 string은 TCHAR용이 따로 없는데 사용하고 싶으시다면 아래와 같이 typedef 해서 쓰시면 됩니다.
typedef std::basic_string<TCHAR > _tstring;



CAtlString, CAtlStringA, CAtlStringW
이 둘은 MFC에 있던 CString과 CStringW가 ATL로 분리되서 나온 아이들 입니다. CAtlString은 TCHAR를 담고 CAtlStringA은 char를 CAtlStringW는 wchar_t를 담아서 씁니다. 이 아이들은 STL의 string과 비슷한 동작을 하는데 Format()이라던지 Trim()과 같은 몇몇 편리한 함수가 있습니다. 단 표준이 아니라 윈도우 환경에서만 쓸 수 있다는 것이 문제 입니다. 다양한 기능들에 대해서는 웹을 더 참고하시기 바랍니다. 여기서는 이정도만 다루고 컨버팅 글에서 좀더 이야기 해보도록 하겠습니다.



BSTR과 CComBSTR
이 아이는 Windows의 COM을 위해서 탄생한 아이입니다. 다른 언어로 개발된 프로그램과 문자열을 주고 받으려는 목적으로 개발되었죠. 기본적으로 wchar_t* 형태로 typedef된 UNICODE입니다만 특이한 점은 문자열의 길이를 문자열 가장 앞 4바이트 공간에 저장한다는 점입니다. 러시안 페인트공의 문제가 해결되지만 대신 숫자가 틀리지 않게 하는데 노력이 들어갑니다. 이를 좀더 사용하기 쉽게 감싼 녀석이 _bstr_t 과 CComBSTR 입니다. 역시 이 타입들도 컨버팅에 관한 이야기만 따로 하도록 하겠습니다.

[출처] C++의 다양한 string 타입 (차니의 컴퓨터 마을) |작성자 newchany

'Program C/C++' 카테고리의 다른 글

Class 내부 Thread basic...  (1) 2013.05.10
typedef...  (0) 2011.09.26
c 표준 함수들  (0) 2011.06.24
문자열 타입 변환 | C &amp; C++  (0) 2010.10.13
About String  (0) 2005.08.05