字符、字符串、常用類型轉換簡介

1          簡介

開發過程中,我常常會遇到ASCIIUnicode以及MBCS不同型的字符串,而且還需要經常進行轉換操作。本文先介紹字符的編碼方式、各種基本字符串型,然後明相的幫助,如CComBSTR、_bstr_t、CString和basic_string等,最後討論們之間以及和其他的數據類型轉換方法

 

2          ANSI、MBCS與UNICODE

ASCII碼(American Standard Code for Information Interchange)由一個字節中的7位(bit)表示8位沒有被使用,範圍是0x00 - 0x7F 共128個字符。其中32--127表示字符32 是空格,32 以下是控制字符(不可見)。

後來擴展了ASCII的定義,ANSI(American National Standards Institute)使用的是一個字節的全部8位(bit)來表示字符,也就具有了 256個字符元;新擴展出來的字符主要是控制字符ANSI字符集最大一個特點是向下兼容ASCII並且留有空餘位置處理一些特殊字符。

後來,很多語系(尤其是非拉丁語,比如日文、韓文、阿拉伯文、臺灣繁體都使用類似的方法擴展了本地字符集的定義,現在統一稱爲 MBCS 字符集(多字節字符集)。這種系統中,有些字符佔用 1 字節,有些 2 字節。這個方法是有缺陷的,因爲各個國家地區定義的字符集有交集,因此如果使用簡體中文GB-2312的軟件,就不能在臺灣繁體BIG-5的環境下運行(顯示亂碼),反之亦然。

當然,提到MBCS,就不得不提一下Code Page,即代碼頁。字符必須編碼後才能被計算機處理,那麼計算機使用的編碼方式就叫做Code Page。比如適用於簡體中文的GB2312和用於繁體中文的big5。

爲了把全世界人民所有的文字符號都統一進行編碼,於是制定了UNICODE標準字符集(寬字符集)。UNICODE 使用2個字節表示一個字符(unsigned short int、WCHAR、_wchar_t、OLECHAR),它的範圍是0x0000 - 0xFFFF 共6萬多個字符。這樣,它把在這個星球上的每一個合理的文字系統整合成了一個單一的字符集,全世界任何一個地區的軟件,可以不用修改地就能在另一個地區運行了。

 

在標準C++中,可以這樣定義一個MBCS或者ANSI的字符串,即:

char* pc = "zhs";

char c = 'z';

 

定義一個UNICODE的字符串,可以用L前綴,即:

wchar_t* pww = L"zhs";

wchar_t ww = L'z';

 

總結一下:

ASCII長度爲7位,共有128個字符,我們常用的英文字母和符號都包含在這裏邊。

ANSI長度爲8位,共有256個字符。前128個字符也爲ASCIIASCII和ANSI只是在一些控制符號上有區別。

MBCS長度爲1或2個字節,不確定。

UNICODE16位,即2個字節,被C/C++定義成wchar_t。

 

3          UNICODE的編碼方式

很多人還存在這樣的誤解: Unicode 僅僅是每個字符佔 2個字節,所以一共有 65536 個可能的字符。然而,這是錯誤的。

 

最初的Unicode編碼, 使用兩個字節表示一個字符,包括表示字符串結束符的‘0’也是兩個字節,這種編碼方式叫做UTF-16。那麼 "Hello" 表示爲:

  00 48 00 65 00 6C 00 6C 00 6F 00 00

實際上,還有一種表示方式:

  48 00 65 00 6C 00 6C 00 6F 00 00 00

到底高位字節在前還是低位字節在前面,是兩種不同的模式。這要看特定的 CPU 在何種模式下工作的更快。 所以這兩種都有。詳細請參考:以下是Big-Endian 和 Little-Endian 兩者概念的區別

 

理論上種解決方案很不錯。但是對於英語用戶來說,他們很少使用 00FF 以上的字符, 有些人無法忍受採用雙倍的存儲空間來存儲每個字符而造成的浪費。基於這些原因,很多人決定忽視 Unicode;而另外的人想出了別的方案

然後人們制定了 UTF-8 UTF-8 是用於保存 Unicode的另一套編碼方案。在 UTF-8 中,任何一個 0--127 的字符佔用一個字節。只有 128 以及更大的才佔用 2, 3, 直到 6 個字節。

UTF-8編碼的字符串,其結束符是一個字節的0;而不是UTF-16的兩個字節的0。換句話說,UTF-8編碼的字符串,會向下兼容標準C的函數。

 

實際上,還有一種Unicode編碼形式, 使用個字節表示字符串中的任意一個字符這種編碼方式叫做UTF-32,或者UCS-4

 

默認情況Windows NT操作系統的開發(尤其VC開發),如果選擇的是UNICODE,指的就是UTF-16;如果選擇的是MBCS,則表示字符串中既有單字節字符也有雙字節字符。Intelx86機器上都是little-endian而不是big-endian方式

 

那麼在實際開發中,我們爲什麼要使用UNICODE哪?下列一些情況下使用Unicode將會使你受益

1你的程序只運行在Windows NT系統中。Windows 9x 中大多數的 API 沒有實現 Unicode 版本。所以如果你的程序要在Windows 9x中運行你必使用MBCS APIs。然而由於NT內部都使用Unicode所以使用Unicode APIs將會加快你的程序的運行速度。次,你傳遞一個字符串調MBCS API,操作系會把個字符串轉換Unicode字符串,然後調對應Unicode API。如果一個字符串被返回,操作系統還要把它轉變回去。儘管轉換過程被高度化了,但它速度造成的失是無法避免的。

2你的程序需要處理超過MAX_PATH個字符長的文件名。只要你使用Unicode API,NT系使用非常的文件名(突破了MAX_PATH的限制,MAX_PATH=260)。使用Unicode API的另一個點是你的程序會自動處理用戶輸入的各種語言。所以一個用可以入英文,中文或者日文,而你不需要寫代理它

3你的程序需要使用XP中引入的只有Unicode版本的API 最後,隨着windows 9x品的淡出,微似乎正在拋棄MBCS APIs。例如,包含兩個字符串參數的SetWindowTheme() API只有Unicode版本的。使用Unicode來build你的程序將會化字符串的理,你不必在MBCS和Unicode之相互轉換

 

總結一下幾種 Unicode 的表示方法

傳統的雙字節表示方法, 稱爲 UCS-2(因爲有 2 個字節) 或者 UTF-16(因爲有 16 個位),但是你要搞清楚是高位在前的,還是高位在後的 UCS-2。

還有一種就是 UTF-8。 如果你的程序只使用英文的話,它仍然會工作正常。

另外還有 UCS-4, 儲存每一個字符爲 4 個字節。它的優點是每一個字符都保存爲同樣長的。但很明顯,缺點是浪費太多存儲空間了。

 

以下是Big-Endian 和 Little-Endian 兩者概念的區別,僅供參考。

Big-Endian 和 Little-Endian 字節排序

字節排序

含義

Big-Endian

一個Word中的高位的Byte放在內存中這個Word區域的低地址處。

Little-Endian

一個Word中的低位的Byte放在內存中這個Word區域的低地址處。

必須注意的是:表中一個Word的長度是16位,一個Byte的長度是8位。如果一個數超過一個Word的長度,必須先按Word分成若干部分,然後每一部分(即每個Word內部)按Big-Endian或者Little-Endian的不同操作來處理字節。

一個例子:

如果我們將0x1234abcd(這是一個INT32類型的數據)寫入到以0x0000開始的內存中,則結果爲:

           big-endian     little-endian
0x0000     0x12              0xcd
0x0001     0x34              0xab
0x0002     0xab              0x34
0x0003     0xcd              0x12
(注意:0xab換算成2進制是10101011,是個8位的數。)

 

4          T和TCHAR

因爲我們開發面向的是具有多字節字符的中文或者日文,那麼我們可能會有MBCS和UNICODE這兩種選擇。實際開發中,我們希望把MBCS和Unicode的區別透明化,也就是說不應該到處都有這樣的代碼:

#ifdef _UNICODE

 wchar_t ch;

#else

 char ch;

#endif

 

爲了簡化開發,微軟運行時庫提供了一系列的映射,把很多字符串相關的數據類型、函數和對象給映射成了T類型,這些映射的宏都在TCHAR.H中。要決定程序中應該使用的編碼方式,可以通過VC環境中設定預編譯的宏:

Project->Settings...->C/C++ tab頁->Preprocessor definitions

對應於MBCS, Unicode,和ASCII (SBCS),分別設置爲:MBCS、UNICODE,_UNICODE 和空(什麼也不設)。

#define

Compiled version

Example

_UNICODE

Unicode (wide-character)

_tcsrev maps to _wcsrev

_MBCS

Multibyte-character

_tcsrev maps to _mbsrev

None ( neither _UNICODE nor _MBCS defined)

SBCS (ASCII)

_tcsrev maps to strrev

 

以下是一些T類型的定義在實際系統中的對應:

Generic-text
data type name

_MBCS
defined

_UNICODE
defined

_TCHAR

char

wchar_t

_tfinddata_t

_finddata_t

_wfinddata_t

_tfinddata64_t

__finddata64_t

__wfinddata64_t

_tfinddatai64_t

_finddatai64_t

_wfinddatai64_t

_TSCHAR

signed char

wchar_t

_TUCHAR

unsigned char

wchar_t

_TXCHAR

unsigned char

wchar_t

_T or _TEXT

No effect (removed by preprocessor)

L(converts following character or string to its Unicode counterpart)

 

對於字符串的數據類型,我們可以使用TCHAR

#ifdef _UNICODE

 typedef wchar_t TCHAR;

#else

 typedef char TCHAR;

#endif

所以,TCHAR中在MBCS程序中是char類型,在Unicode中是 wchar_t 類型。

 

對於字符串常量,有個 _T() 宏,用於解決 L 前綴:

#ifdef _UNICODE

 #define _T(x) L##x

#else

 #define _T(x) x

#endif

## 是預處理算子,將二個變量粘貼在一起。宏_T有幾種形式,功能都相同。如: -- TEXT, _TEXT, __TEXT, 和 __T這四種宏的功能相同。

不管什麼時候都應該對字符串用 _T 宏處理,這樣就可以在Unicode編碼中給字符串加上L前綴,而對ANSI則沒有任何影響

TCHAR szNewText[] = _T("we love Bob!");

 

另外,Microsoft還擴展了一些常用的字符和字符串類型,其中,P表示pointC表示constT表示TCHARW表示wide。如下表所示:

type

Meaning in MBCS builds

Meaning in Unicode builds

WCHAR

wchar_t

wchar_t

LPSTR

zero-terminated string of char (char*)

zero-terminated string of char (char*)

LPCSTR

constant zero-terminated string of char (const char*)

constant zero-terminated string of char (const char*)

LPWSTR

zero-terminated Unicode string (wchar_t*)

zero-terminated Unicode string (wchar_t*)

LPCWSTR

constant zero-terminated Unicode string (const wchar_t*)

constant zero-terminated Unicode string (const wchar_t*)

TCHAR

char

wchar_t

LPTSTR

zero-terminated string of TCHAR (TCHAR*)

zero-terminated string of TCHAR (TCHAR*)

LPCTSTR

constant zero-terminated string of TCHAR (const TCHAR*)

constant zero-terminated string of TCHAR (const TCHAR*)

 

注意,這些映射的定義是微軟擴展的,並非ANSI標準,所以它是不可移植的。

 

5          C風格的字符串,即:字符數組

所有的字符串類都起源於C風格的字符串

C風格字符串是字符的數組,數組中每一個元素保存了一個字符,可以是單字節或者多字節,最後以零字節表示字符串結尾;對於單字節或者MBCS字符串,一個字節0x00結束字符串,對於Unicode,則兩個字節0x0000結束字符串。

C語言字符串處理函數,如strcpy(), sprintf(), atol()等只能用於單字節字符串。在標準庫中有隻用於Unicode字符串的函數,如wcscpy(), swprintf(), _wtol()。微軟在C運行庫(CRT)中加入了對MBCS字符串的支持使用_mbsxxx()函數,所以在處理DBCS字符串(如日語,中文,或其它MBCS)時,就要用_mbsxxx()函數。

 

現在用一個示例來說明字符串處理函數的不同。如有Unicode字符串L"Bob":

對於strxxx() 和 _mbsxxx() 函數族中的字符串長度測量函數,它們都返回字符串的字符數。如果字符串含有3個雙字節字符,_mbslen()將返回3Unicode的函數返回的是wchar_t的數量,如wcslen(L"Bob") 返回3。

這時如用strlen()函數求字符串的長度就發生問題。函數找到第一個字節42,然後是00,意味着字符串結尾,於是返回1。而如果用Unicode的函數,wcslen(L"Bob"),則返回3,因爲他判斷的是雙字節的00。

 

我們中的大多數人都是從單字節字符集成長過來的,都習慣於用指針的 ++ 和 -- 操作符來遍歷字符串,有時也使用數組來處理字符串中的字符。這二種方法對於純粹的單字節字符串 Unicode 字符串的操作都是正確無誤的,因爲二者的字符都是等長的,編譯器能夠的正確返回我們尋求的字符位置。

但對於MBCS字符串就不能這樣了,因爲每一個字符的長度不確定所以,用指針訪問MBCS字符串不可使用 ++ 算子,除非每次都檢查是否爲前導字節絕不可使用 -- 算子來向後遍歷。正確的方法是用MBCS函數將指針指向恰當的字符位置,比如CharPrev()CharNext()函數,它會根據情況而決定移動一個或者兩個字節

 

6          STL的string、wstring和其他

STL只有一個字符串類,即basic_string模板類,basic_string管理一個零結尾的字符數組,字符類型char或者wchar_t由模板參數決定。

basic_string預定義了二個特例:string,含有char類型字符;wstring,含有wchar_t類型字符。這兩個類型是如下定義的:

   typedef basic_string<wchar_t> wstring;

   typedef basic_string<char> string;

沒有內建的TCHAR特例,可用下面的代碼自己實現:

   typedef basic_string<TCHAR> tstring;

 

basic_string模板類提供了和CString一樣的功能,而且還有其他的優勢:CString(是MFC的)的效率高;它是標準C++庫的內容,易於移植

 

如何使用string和wstring。

首先需要包含頭文件<string>,然後引用名字空間using namespace std;之後就像普通的類一樣使用。比如,要使用replace功能,可以如下使用:

#include <string>

 

{

   using namespace std;

 

   string result1a, result1b;

   string s1o ( "AAAAAAAA" );

   string s1p ( "BBB" );

   result1a = s1o.replace ( 1 , 3 , s1p );

}

關於詳細介紹,請參看另外的一篇文檔< STLString.doc >

關於詳細介紹,還可以參看另外的一篇文檔< The C++ Programming Language, by Bjarne Stroustrup >,第20”Stgrings”

 

7          MFC中的CString及其他

CStringMFC庫提供的類,它保存並管理一個TCHAR數組,它的實際字符類型取決於預處理標記的設置。

CString比STL字符串優越的是它的構造函數接受MBCS和Unicode字符串,並且可以轉換爲LPCTSTR,因此可以向接受LPCTSTR的函數直接傳遞CString對象,不必std::string那樣調用c_str()方法;另外,它還可以從字符資源表中加在字符串來進行構造。

 

CString的使用,要比string簡單,因爲如果我們創建了一個VC工程,默認的就已經包含了CString的類,而且也不需要引用名字空間。

 

// 構造

CString s1 = "char string"; // 從LPCSTR構造

CString s2 = L"wide char string"; // 從LPCWSTR構造

CString s3 ( ' ', 100 ); // 預分配100字節,填充空格

CString s4 = "New window text";

CString s6, s7;

s6.LoadString ( IDS_SOME_STR ); // 從字符串表加載

s7.Format ( IDS_SOME_FORMAT, "bob", nSomeStuff, ... ); // 從字符串表加載打印格式的字符串

 

//顯示或者隱式的類型轉化

SetWindowText ( hwndSomeWindow, s4 );   // 隱式轉化成LPCTSTR

SetWindowText ( hwndSomeWindow, (LPCTSTR) s4 ); // 或者,顯式地做強制類型轉換

 

注意,CString只允許一種強制類型轉換,即強制轉換爲LPCTSTR。強制轉換爲LPTSTR (非常量指針)是錯誤的。按照老習慣,將CString強制轉換爲LPTSTR只能傷害自己。有時在程序中沒有發現出錯,那只是碰巧。轉換到非常量指針的正確方法是調用GetBuffer()方法,這種方法是把CString中的字符串指針拿出來,如果用戶修改這段內容,CString中的內容也相應地被修改了,這種方法在沒有強烈需求的情況下也儘量不使用。進行非常量的強制類型轉換,打破了面向對象的封裝原則,並逾越了CString的內部操作,容易出Bug

 

關於詳細介紹,請參看另外的一篇文檔< CStringClass.doc >

 

8          COM中的BSTR、CComBSTR、_bstr_t和其他

COM中對字符串有特殊的要求,即:寬字符,告知對方字符串的長度。

 

OLECHARwin32中爲16位,即2個字節,被C/C++定義成wchar_t

OLECHARwin16中爲8位,即1個字節,被C/C++定義成char

當然,我們的開發都是基於Win32的,所以我們在開發過程中會認爲OLECHAR就是被定義成了wchar_t

 

8.1BSTR

BSTR是一個指向UNICODE 字符串的指針,這個字符串可以沒有結束符,在BSTR向前的4個字中,使用DWORD保存着個字符串的字節長度。也就是說,它的長度不是由NULL結尾字符決定,而是由長度前綴決定,雖然BSTR也可能有NULL字符在內部或者結尾處。它主要用在COM以及Automation方面。

看看定義就知道了:

typedef wchar_t WCHAR;

typedef WCHAR OLECHAR;

typedef OLECHAR* BSTR;

 

注意:BSTR不等於OLECHAR*,最大的區別在於它有一個長度前綴。

 

通俗地說,你不能直接把一個內存指針直接作爲參數傳遞給COM函數。系統需要把這塊內存的內容傳遞到地球另一邊的計算機上,因此,系統至少需要知道你這塊內存的尺寸,也就是說,需要傳遞多少字節給用戶。而字符串又是非常常用的一種類型,因此 COM 設計者引入了BSTR。因此係統就能夠正確處理並傳送這個字符串到地球另一 邊了。特別需要注意的是,由於BSTR的指針就是指向 UNICODE 串,因此 BSTR  LPOLESTR 可以在一定程度上混用,但是不建議這樣做。

 

關於BSTR的詳細介紹,請參看另外的一篇文檔< BSTR.doc >

8.2_bstr_t

_bstr_t C++運行時庫提供的對BSTR的完全包裝類。實際上,它隱含了BSTR的大多數細節,它提供多種構造函數,能夠處理隱含的C類型字符串以及BSTR字符串。它雖然提供了BSTR的處理機制,但是不能作爲COM方法的輸出參數[out],因爲他的操作符重載只有wchar_t而沒有BSTR;他可以作爲輸入傳遞給需要BSTR數據的函數,但是,不建議這樣做。如果要用到BSTR* 類型數據,用ATLCComBSTR類更爲方便

 

_bstr_t使用示例如下:

// 構造

_bstr_t bs1 = _T("wide char string"); // LPCWSTR構造

_bstr_t bs3 = bs1;              // 拷貝另一個 _bstr_t

 

// 數據萃取

LPCTSTR pwsz1 = bs1;            // 返回內部的Unicode字符串

BSTR    bstr = bs1.copy();      // 拷貝bs1, 返回BSTR

SysFreeString ( bstr );

 

8.3CComBSTR

CComBSTR ATLBSTR包裝類,某些情況下比_bstr_t 更有用。最主要的是,CComBSTR允許操作隱含BSTR,就是說,傳遞一個CComBSTR對象給COM方法時,CComBSTR對象會自動管理BSTR內存。例如,要調用下面的接口函數:

// 簡單接口

struct IStuff : public IUnknown

{

  STDMETHOD(SetText)(BSTR bsText);

  STDMETHOD(GetText)(BSTR* pbsText);

};

CComBSTR 有一個BSTR操作方法,能將BSTR直接傳遞給SetText()。還有一個引用操作(operator &)方法,返回BSTR*,將BSTR*傳遞給需要它的有關函數。

CComBSTR bs1;

CComBSTR bs2 = _T("new text");

pStuff->GetText ( &bs1 );       // ok, 取得內部BSTR地址

pStuff->SetText ( bs2 );        // ok, 調用BSTR轉換

pStuff->SetText ( (BSTR) bs2 ); // cast ok, 同上

 

CComBSTR有類似於 _bstr_t 的構造函數。但沒有內建MBCS字符串的轉換函數。可以調用ATL宏進行轉換。

// 構造

CComBSTR bs3 = bs1; // 拷貝CComBSTR

CComBSTR bs4;

bs4.LoadString ( IDS_SOME_STR ); // 從字符串表加載

// 數據萃取

BSTR bstr1 = bs1; // 返回內部BSTR,但不可修改!

BSTR bstr2 = (BSTR) bs1; // cast ok, 同上

BSTR bstr3 = bs1.Copy(); // 拷貝bs1, 返回BSTR

BSTR bstr4;

bstr4 = bs1.Detach(); // bs1不再管理它的BSTR

// ...

SysFreeString ( bstr3 );

SysFreeString ( bstr4 );

 

上面的最後一個示例用到了Detach()方法。該方法調用後,CComBSTR對象就不再管理它的BSTR或其相應內存。所以bstr4就必須調用SysFreeString()

最後討論一下引用操作符(operator &)。它的超越使得有些STL集合(list)不能直接使用CComBSTR。在集合上使用引用操作返回指向包容類的指針。但是在CComBSTR上使用引用操作,返回的是BSTR*,不是CComBSTR*。不過可以用ATLCAdapt類來解決這個問題。例如,要建立一個CComBSTR的隊列,可以聲明爲:

  std::list< CAdapt<CComBSTR>> bstr_list;

CAdapt 提供集合所需的操作,是隱含於代碼的。這時使用bstr_list 就象在操作一個CComBSTR隊列。

 

關於詳細介紹,請參看另外的一篇文檔< CCOMBSTR.doc >,目前還沒有寫完。

 

9          字符串的處理函數以及常用算法

首先介紹一些通用函數命名規則:

字符集

特性

實例

ANSI

操作函數以str開頭

strcpy

Unicode

操作函數以wcs開頭

wcscpy

MBCS

操作函數以_mbs開頭

_mbscpy

ANSI/Unicode

操作函數以_tcs開頭(C運行期庫)

_tcscpy

ANSI/Unicode

操作函數以lstr開頭(Windows函數)

lstrcpy

 

下表列出了一些運行時庫的字符串處理API(比較常用的用淺藍色標出)。這些API是針對C風格的字符串的處理函數,如果你用到了CComBSTR_bstr_tCString等,就使用這些類提供的成員函數就好了,各組函數之間在功能上基本上沒有大的差別,只是實現方式的一些差異。

Routine

Use

_mbsdec, _strdec, _wcsdec

Move string pointer back one character

_mbsinc, _strinc, _wcsinc

Advance string pointer by one character

_mbsnextc, _strnextc, _wcsnextc

Find next character in string

_mbsninc. _strninc, _wcsninc

Advance string pointer by n characters

_mbsspnp, _strspnp, _wcsspnp

Return pointer to first character in given string that is not in another given string

_scprintf, _scwprintf

Return the number of characters in a formatted string

_snscanf, _snwscanf

Read formatted data of a specified length from the standard input stream.

sprintf, _stprintf

Write formatted data to a string

strcat, wcscat, _mbscat

Append one string to another

strchr, wcschr, _mbschr

Find first occurrence of specified character in string

strcmp, wcscmp, _mbscmp

Compare two strings

strcoll, wcscoll, _stricoll, _wcsicoll, _strncoll, _wcsncoll, _strnicoll, _wcsnicoll

Compare two strings using current locale code page information (_stricoll, _wcsicoll, _strnicoll, and _wcsnicoll are case-insensitive)

strcpy, wcscpy, _mbscpy

Copy one string to another

strcspn, wcscspn, _mbscspn,

Find first occurrence of character from specified character set in string

_strdup, _wcsdup, _mbsdup

Duplicate string

strerror, _wcserror

Map error number to message string

_strerror, __wcserror

Map user-defined error message to string

strftime, wcsftime

Format date-and-time string

_stricmp, _wcsicmp, _mbsicmp

Compare two strings without regard to case

strlen, wcslen, _mbslen, _mbstrlen

Find length of string

_strlwr, _wcslwr, _mbslwr

Convert string to lowercase

strncat, wcsncat, _mbsncat

Append characters of string

strncmp, wcsncmp, _mbsncmp

Compare characters of two strings

strncpy, wcsncpy, _mbsncpy

Copy characters of one string to another

_strnicmp, _wcsnicmp, _mbsnicmp

Compare characters of two strings without regard to case

_strnset, _wcsnset, _mbsnset

Set first n characters of string to specified character

strpbrk, wcspbrk, _mbspbrk

Find first occurrence of character from one string in another string

strrchr, wcsrchr,_mbsrchr

Find last occurrence of given character in string

_strrev, _wcsrev,_mbsrev

Reverse string

_strset, _wcsset, _mbsset

Set all characters of string to specified character

strspn, wcsspn, _mbsspn

Find first substring from one string in another string

strstr, wcsstr, _mbsstr

Find first occurrence of specified string in another string

strtok, wcstok, _mbstok

Find next token in string

_strupr, _wcsupr, _mbsupr

Convert string to uppercase

strxfrm, wcsxfrm

Transform string into collated form based on locale-specific information

vsprintf, _vstprint

Write formatted output using a pointer to a list of arguments

 

如果使用的是BSTR,那麼如下的一些API需要用到:

String Manipulation Functions

Descriptions

SysAllocString

Creates and initializes a string.

SysAllocStringByteLen

Creates a zero-terminated string of a specified length (32-bit only).

SysAllocStringLen

Creates a string of a specified length.

SysFreeString

Frees a previously created string.

SysReAllocString

Changes the size and value of a string.

SysReAllocStringLen

Changes the size of an existing string.

SysStringByteLen

Returns the length of a string in bytes (32-bit only).

SysStringLen

Returns the length of a string.

CString::AllocSysString()

CString 得到 BSTR

CString::SetSysString()

重新申 BSTR ,並制到 CString

 

如果用到的是STL(標準庫)中的string或者wstring,那麼除了可以使用自己模板類的函數之外,還有一些STL中的強大功能供你使用,這些算法無論從通用性還是效率上,都異常強大。

下表列舉算法庫中的一些函數,注意,在使用之前需要包含<algorithm>

binary_search

Tests whether there is an element in a sorted range that is equal to a specified value or that is equivalent to it in a sense specified by a binary predicate.

copy

Assigns the values of elements from a source range to a destination range, iterating through the source sequence of elements and assigning them new positions in a forward direction.

count

Returns the number of elements in a range whose values match a specified value.

count_if

Returns the number of elements in a range whose values match a specified condition.

equal

Compares two ranges element by element either for equality or equivalence in a sense specified by a binary predicate.

find

Locates the position of the first occurrence of an element in a range that has a specified value.

find_first_of

Searches for the first occurrence of any of several values within a target range or for the first occurrence of any of several elements that are equivalent in a sense specified by a binary predicate to a specified set of the elements.

find_if

Locates the position of the first occurrence of an element in a range that satisfies a specified condition.

for_each

Applies a specified function object to each element in a forward order within a range and returns the function object.

includes

Tests whether one sorted range contains all the elements contained in a second sorted range, where the ordering or equivalence criterion between elements may be specified by a binary predicate.

max

Compares two objects and returns the larger of the two, where the ordering criterion may be specified by a binary predicate.

merge

Combines all the elements from two sorted source ranges into a single, sorted destination range, where the ordering criterion may be specified by a binary predicate.

min

Compares two objects and returns the lesser of the two, where the ordering criterion may be specified by a binary predicate.

partial_sort

Arranges a specified number of the smaller elements in a range into a nondescending order or according to an ordering criterion specified by a binary predicate.

remove

Eliminates a specified value from a given range without disturbing the order of the remaining elements and returning the end of a new range free of the specified value.

remove_copy

Copies elements from a source range to a destination range, except that elements of a specified value are not copied, without disturbing the order of the remaining elements and returning the end of a new destination range.

replace

Examines each element in a range and replaces it if it matches a specified value.

reverse

Reverses the order of the elements within a range.

reverse_copy

Reverses the order of the elements within a source range while copying them into a destination range

search

Searches for the first occurrence of a sequence within a target range whose elements are equal to those in a given sequence of elements or whose elements are equivalent in a sense specified by a binary predicate to the elements in the given sequence.

search_n

Searches for the first subsequence in a range that of a specified number of elements having a particular value or a relation to that value as specified by a binary predicate.

sort

Arranges the elements in a specified range into a nondescending order or according to an ordering criterion specified by a binary predicate.

stable_sort

Arranges the elements in a specified range into a nondescending order or according to an ordering criterion specified by a binary predicate and preserves the relative ordering of equivalent elements.

swap

Exchanges the values of the elements between two types of objects, assigning the contents of the first object to the second object and the contents of the second to the first.

transform

Applies a specified function object to each element in a source range or to a pair of elements from two source ranges and copies the return values of the function object into a destination range.

unique

Removes duplicate elements that are adjacent to each other in a specified range.

 

10     各種類型的字符串之間轉換

常用的字符串類之間的轉換方法是:將源字符串轉換爲C類型字符串指針,然後將該指針傳遞給目標類的構造函數。

 

10.1   TCHAR*轉換成CString

  若將TCHAR*轉換成CString,除了直接賦值外,還可使用CString::Format進行。例如:

TCHAR chArray[] = _T("This is a test");
LPTSTR p = _T("This is a test");
CString theString = chArray;
theString.Format(_T("%s"), chArray);
theString = p;

 

10.2   CString轉換成TCHAR*

  方法一,使用強制轉換。例如:

CString theString(_T("This is a test") );
LPTSTR lpsz =(LPTSTR)(LPCTSTR)theString;

 

  方法二,使用strcpy。例如:

CString theString(_T("This is a test") );
LPTSTR lpsz = new TCHAR[theString.GetLength()+1];
_tcscpy(lpsz, theString);

 

  方法三,使用CString::GetBuffer。例如:

CString s(_T("This is a test "));
LPTSTR p = s.GetBuffer();
//
裏添加使用p的代

if(p != NULL) *p = _T('/0');
s.ReleaseBuffer();

// 使用完後及時釋放,以便能使用其它的CString函數

 

10.3   TCHAR*轉換成BSTR

  方法一,使用SysAllocStringAPI函數。例如:

BSTR bstrText = ::SysAllocString(_T("Test"));
BSTR bstrText = ::SysAllocStringLen(_T("Test"),4);
BSTR bstrText = ::SysAllocStringByteLen(_T("Test"),4);


  方法二,使用_bstr_t,這是一種最簡單的方法。例如:

BSTR bstrText = _bstr_t(_T("This is a test"));


  方法三,使用CComBSTR。例如:

BSTR bstrText = CComBSTR(_T("This is a test"));


  或

CComBSTR bstr(_T("This is a test"));
BSTR bstrText = bstr.m_str;


  方法四,使用ConvertStringToBSTR。例如:

TCHAR* lpszText = _T("Test");
BSTR bstrText = _com_util::ConvertStringToBSTR(lpszText);

 

10.4   BSTR轉換成TCHAR*

  方法一,使用ConvertBSTRToString。例如:

BSTR bstrText = ::SysAllocString(_T("Test"));
TCHAR* lpszText2 = _com_util::ConvertBSTRToString(bstrText);
SysFreeString(bstrText);

// 用完
delete[] lpszText2;


  方法二,使用_bstr_t的賦值運算符重載。例如:

_bstr_t b = bstrText;
TCHAR* lpszText2 = b;

 

10.5   CString轉換成BSTR

CStringAllocSysString()和SetSysString()能夠從CString中得到BSTR,並在必要時轉換成Unicode。除了SetSysString()使用BSTR*參數外,二者一樣。他們內部調用了SysAllocString,所以由他們創建的BSTR,需要使用者顯式的釋放SysFreeString。

 

例如:

CString s5 = _T("Bob!");

BSTR bs1 = NULL, bs2 = NULL;

bs1 = s5.AllocSysString();

s5.SetSysString ( &bs2 );

// ...

SysFreeString ( bs1 ); // 用完

SysFreeString ( bs2 );

 

10.6   BSTR轉換成CString

  一般可按下列方法進行

BSTR bstrText = ::SysAllocString(_T("Test"));
CString str = bstrText;

  或

CStringA str(bstrText);

 

10.7   basic_string轉化成TCHAR*

  使用basic_string::c_str()方法轉化成C風格的字符串。

 

10.8   ANSIUnicode和寬字符之間的轉換

  方法一,使用MultiByteToWideChar將MBCS字符轉換成Unicode字符,使用WideCharToMultiByte將Unicode字符轉換成ANSI字符。

  方法二,使用“_T”將ANSI轉換成“一般”類型字符串,使用“L”將ANSI轉換成Unicode。例如:

TCHAR tstr[] = _T("this is a test");
wchar_t wszStr[] = L"This is a test";


  方法三,使用ATL 3.0(即VC6.0ATL的轉換宏和類。
ATL的字符串轉換宏可以方便地轉換不同編碼的字符,用在函數中很有效。宏按照[source type]2[new type] 或 [source type]2C[new type]格式命名。後者轉換爲一個常量指針 (名字內含"C")。

 

ATL的轉換宏:

A2BSTR

OLE2A

T2A

W2A

A2COLE

OLE2BSTR

T2BSTR

W2BSTR

A2CT

OLE2CA

T2CA

W2CA

A2CW

OLE2CT

T2COLE

W2COLE

A2OLE

OLE2CW

T2CW

W2CT

A2T

OLE2T

T2OLE

W2OLE

A2W

OLE2W

T2W

W2T


上表中的宏函數縮寫含義如下:

2

表示轉換爲、轉換到的含義。

A

MBCS字符串,char* (A for ANSI)

WOLE

寬字符串。也就是 UNICODEwchar_t* (W for wide)OLECHAR字符串OLECHAR* (實際等於W)

T

TCHAR字符串,TCHAR*。如果定義了_UNICODE,則T表示W;如果定義了 _MBCS,則T表示A

C

const 的縮寫

BSTR

BSTR (只用於目的類型)

 

例如,W2A() 將Unicode字符串轉換爲MBCS字符串,T2CW()將TCHAR字符串轉換爲Unicode字符串常量。

要使用宏轉換,程序中要包含atlconv.h頭文件。可以在非ATL程序中使用宏轉換,因爲頭文件不依賴其它的ATL,也不需要 _Module全局變量。如在函數中使用轉換宏,在函數起始處先寫上USES_CONVERSION宏它表明某些局部變量由宏控制使用。

轉換得到的結果字符串,只要不是BSTR,都存儲在堆棧中。如果要在函數外使用這些字符串,就要將這些字符串拷貝到其它的字符串類。如果結果是BSTR,內存不會自動釋放,因此必須將返回值分配給一個BSTR變量或BSTR的包裝類,以避免內存泄露,或者自己處理BSTR的內存

 

示例:

// 帶有字符串的函數:

void Foo ( LPCWSTR wstr );

void Bar ( BSTR bstr );

// 返回字符串的函數:

void Baz ( BSTR* pbstr );

#include <atlconv.h>

 

{

using std::string;

USES_CONVERSION;    // 聲明局部變量由宏控制使用

 

// 示例1:送一個MBCS字符串到Foo()

LPCSTR psz1 = "Bob";

string str1 = "Bob";

Foo ( A2CW(psz1) );

Foo ( A2CW(str1.c_str()) );

 

// 示例2:將MBCS字符串和Unicode字符串送到Bar()

LPCSTR psz2 = "Bob";

LPCWSTR wsz = L"Bob";

BSTR bs1;

CComBSTR bs2;

bs1 = A2BSTR(psz2);         // 創建 BSTR

bs2.Attach ( W2BSTR(wsz) ); // 同上,分配到CComBSTR

Bar ( bs1 );

Bar ( bs2 );

SysFreeString ( bs1 );      // 釋放bs1

// 不必釋放bs2,由CComBSTR釋放。

 

// 示例3:轉換由Baz()返回的BSTR

BSTR bs3 = NULL;

string str2;

Baz ( &bs3 );          // Baz() 填充bs3內容

str2 = W2CA(bs3);      // 轉換爲MBCS字符串

SysFreeString ( bs3 ); // 釋放bs3

}

 

10.9   寬字符和MBCS的轉化

這裏所說的MBCS,實際上是指一個字符串中既含有單字節還有雙字節的字符。一般來說,使用WideCharToMultiByte()MultiByteToWideChar()來進行相互的轉化。

 

    1. 函數WideCharToMultiByte (),轉換UNICODEMBCS。使用範例:

    LPCOLESTR lpw = L"Hello,你好";

    size_t wLen = wcslen( lpw ) + 1;  // 寬字符字符長度,+1表示包含字符串結束符

   

    int aLen=WideCharToMultiByte(  //計算所需 MBCS 字符串字節長度

                CP_ACP,

                0,

                lpw,  // 寬字符串指針

                wLen, // 字符長度

                NULL,

                0,  // 參數0表示計算轉換後的字符空間

                NULL,

                NULL);

       

      LPSTR lpa = new char [aLen];

      WideCharToMultiByte(  //進行轉化

                CP_ACP,

                0,

                lpw,

                wLen,

                lpa,  // 轉換後的字符串指針

                aLen, // 給出空間大小

                NULL,

                NULL);

 

      // 此時,lpa 中保存着轉換後的 MBCS 字符串

      ... ... ... ...

      delete [] lpa;


    2
、函數 MultiByteToWideChar(),轉換 MBCS UNICODE。使用範例:

      LPCSTR lpa = "Hello,你好";

      size_t aLen = strlen( lpa ) + 1;

     

      int wLen = MultiByteToWideChar(

                CP_ACP,

                0,

                lpa,

                aLen,

                NULL,

                0);

     

      LPOLESTR lpw = new WCHAR [wLen];

      MultiByteToWideChar(

                CP_ACP,

                0,

                lpa,

                aLen,

                lpw,

                wLen);

      ... ... ... ...

      delete [] lpw;

 

關於這兩個函數的第一個參數,即CP_ACP,就是要指定Code Page,可選的Code Page,比如:

Value

Meaning

CP_ACP

ANSI code page

CP_MACCP

Macintosh code page

CP_OEMCP

OEM code page

CP_SYMBOL

Windows 2000/XP: Symbol code page (42)

CP_THREAD_ACP

Windows 2000/XP: The current thread's ANSI code page

CP_UTF7

Windows 98/Me, Windows NT 4.0 and later: Translate using UTF-7

CP_UTF8

Windows 98/Me, Windows NT 4.0 and later: Translate using UTF-8.

其他的Code Page,在使用的時候情查詢MSDNCode-Page Identifiers以獲取更多和更詳細的信息。

 

11     String和其他數據類型(數字類型)的轉化

這裏所說的其他類型主要是數字類型。

 

下表列出了一些運行時庫的字符串轉化處理API

Routine

Use

_ecvt

Convert double to string of specified length

_fcvt

Convert double to string with specified number of digits following decimal point

_gcvt

Convert double number to string; store string in buffer

_itot, _i64tot

Convert int to string

_ltot

Convert long to string

strtod, wcstod

Convert string to double

strtol, wcstol

Convert string to long integer

strtoul, wcstoul

Convert string to unsigned long integer

atof,_wtof

Convert string to a double

_ttoi, _ttoi64

Convert string to int or __int64

_ttol

Convert string to long

 

當然把其他類型轉化成字符串,還有printf()wprintf()函數來使用。

 

有一些字符串類,如CComBSTR或者_bstr_t等,本身不提供和其他數據類型相互轉化的功能(比如,format),所以只能先把字符串數據取出來,轉化成C風格的字符串,然後調用運行時庫的API來進行轉化;或者先把其他類型數據轉化成字符串類型,然後通過API來轉化。

 

12     一些建議

Win32平臺上開發,有一些共同的原則要遵循。

 

1.       UNICODE方式編譯

應該設置爲_UNICODE方式編譯。設置條件編譯的方式是:VC6中,Project->Settings...->C/C++ tab->Preprocessor definitions中把_MBCS修改爲_UNICODE,UNICODE。同時,在ProjectSetting/link/output 中設置EntrywWinMainCRTStartup

注意:_UNICODEUNICODE是不同的。_UNICODE宏用於C運行期頭文件,UNICODE宏則用於Windows頭文件。但是一般我們並不區分這種區別,而是籠統的把這兩個宏都定義上。

如果用的是UNICODE字符,調試中經常發現只能看到第一個字符的問題,可以作如下設置就可以看到全部的字符串了:

Tools->Options...->Debug tab->Display Unicode strings

打上勾就好了。

 

2.       使用T類型

在實際編碼中,必須使用T類型,這是非常好的習慣,必須遵守

//1.將CString >> TCHAR
CString str1 = TEXT("將CString >> TCHAR");
TCHAR sz1[64] = {0};
_tcscpy(sz1,str1);
AfxMessageBox(sz1);
//2.將CString >> LPTSTR(有三種方法)
CString str2_1 = TEXT("將CString >> LPTSTR 第一種");
LPTSTR lpsz2_1 = (LPTSTR)(LPCTSTR)str2_1;
AfxMessageBox(lpsz2_1);
//
CString str2_2 = TEXT("將CString >> LPTSTR 第二種");
LPTSTR lpsz2_2 = new TCHAR[str2_2.GetLength()+3];
_tcscpy(lpsz2_2,str2_2);
AfxMessageBox(lpsz2_2);
delete[] lpsz2_2;//用完後,釋放內存
AfxMessageBox(lpsz2_2);
//
CString str2_3 = TEXT("將CString >> LPTSTR 第三種");
LPTSTR lpsz2_3 = str2_3.GetBuffer(str2_3.GetLength()+3/*buffer最小長度*/);
str2_3.ReleaseBuffer();
AfxMessageBox(lpsz2_3);
//3.將TCHAR >> CString
TCHAR sz3[64] = TEXT("將TCHAR >> CString");
CString str3;
str3 = sz3;
AfxMessageBox(str3);
//4.將LPTSTR >> CString
LPTSTR lpsz4 = TEXT("將LPTSTR >> CString");
CString str4 = lpsz4;
AfxMessageBox(str4);
//5.將TCHAR >> LPTSTR
TCHAR sz5_1[64] = TEXT("將TCHAR >> LPTSTR 第一種 555");
LPTSTR lpsz5_1 = sz5_1;
AfxMessageBox(lpsz5_1);
//
TCHAR sz5_2[64] = TEXT("將TCHAR >> LPTSTR 第二種 555");
LPTSTR lpsz5_2 = new TCHAR[lstrlen(sz5_2)+3];
_tcscpy(lpsz5_2,sz5_2);
AfxMessageBox(lpsz5_2);
delete[] lpsz5_2;
AfxMessageBox(lpsz5_2);
//6.將LPTSTR >> TCHAR
LPTSTR lpsz6 = TEXT("將LPTSTR >> TCHAR 666");
TCHAR sz6[64] = {0};
_tcscpy(sz6,lpsz6);
AfxMessageBox(sz6);

//結論:
//1.把CString看成LPTSTR,如str.GetBuffer(str.GetLength()+3)就是一個指針,而GetBuffer的參數爲指針長度.
//2.當給LPTSTR賦值時,只能用=,如 LPTSTR sz = str(上面結論裏得到CString是指針,指針可以給值給指針),
//也可以用_tcscpy,因_tcscpy是內存操作,所以要給LPTSTR分配內存指針長度,如LPTSTR lpsz = new TCHAR[lstrlen(sz)+3].
//3.當給TCHAR賦值時,只能用_tcscpy(sz,X);
//4.當TCHAR爲賦值者時(被給值不爲TCHAR),如 LPTSTR lpsz = sz 或者 CString str = sz,這裏sz被看成是字符指針.

//約定
//sz 表示 TCHAR
//lpsz 表示 LPTSTR
//lpcsz 表示 LPCTSTR
//str 表示 CString

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章