我日常整理了CString的一些用法,很實用,發給你共享,相信你之後遇到CString的問題都會迎刃而解:

CString 是一種頗有用的數據類型。它們很大程度上簡化了MFC中的許多操做,使得MFC在作字符串操做的時候方便了不少。無論怎樣,使用CString有不少特殊的技巧,特別是對於純C背景下走出來的程序員來講有點難以學習。這篇文章就來討論這些技巧。
  使用CString可讓你對字符串的操做更加直截了當。這篇文章不是CString的徹底手冊,但囊括了大部分常見基本問題。
這篇文章包括如下內容: 
1. CString 對象的鏈接 
2. 格式化字符串(包括 int 型轉化爲 CString ) 
3. CString 型轉化成 int 型 
4. CString 型和 char* 類型的相互轉化
5. char* 轉化成 CString 
6. CString 轉化成 char* 之一:使用LPCTSTR強制轉化 
7. CString 轉化成 char* 之二:使用CString對象的GetBuffer方法 
8. CString 轉化成 char* 之三: 和控件的接口
9. CString 型轉化成 BSTR 型; 
10. BSTR 型轉化成 CString 型; 
11. VARIANT 型轉化成 CString 型; 
12. 載入字符串表資源; 
13. CString 和臨時對象; 
14. CString 的效率; 
15. 總結 
下面我分別討論。
 一、CString 對象的鏈接
  能體現出 CString 類型方便性特色的一個方面就字符串的鏈接,使用 CString 類型,你能很方便地鏈接兩個字符串,正以下面的例子:
CString gray("Gray");
CString cat("Cat");
CString graycat = gray + cat;
要比用下面的方法好得多:
char gray[] = "Gray";
char cat[] = "Cat";
char * graycat = malloc(strlen(gray) + strlen(cat) + 1);
strcpy(graycat, gray);
strcat(graycat, cat);
 二、格式化字符串
  與其用 sprintf() 函數或 wsprintf() 函數來格式化一個字符串,還不如用 CString 對象的Format()方法:
CString s;
s.Format(_T("The total is %d"), total);
  用這種方法的好處是你不用擔憂用來存放格式化後數據的緩衝區是否足夠大,這些工做由CString類替你完成。
  格式化是一種把其它不是字符串類型的數據轉化爲CString類型的最經常使用技巧,好比,把一個整數轉化成CString類型,可用以下方法:
CString s;
s.Format(_T("%d"), total);
  我老是對個人字符串使用_T()宏,這是爲了讓個人代碼至少有Unicode的意識,固然,關於Unicode的話題不在這篇文章的討論範圍。_T()宏在8位字符環境下是以下定義的:
#define _T(x) x // 非Unicode版本(non-Unicode version)
而在Unicode環境下是以下定義的:
#define _T(x) L##x // Unicode版本(Unicode version)
因此在Unicode環境下,它的效果就至關於:
s.Format(L"%d", total);
  若是你認爲你的程序可能在Unicode的環境下運行,那麼開始在乎用 Unicode 編碼。好比說,不要用 sizeof() 操做符來得到字符串的長度,由於在Unicode環境下就會有2倍的偏差。咱們能夠用一些方法來隱藏Unicode的一些細節,好比在我須要得到字符長度的時候,我會用一個叫作DIM的宏,這個宏是在個人dim.h文件中定義的,我會在我寫的全部程序中都包含這個文件:
#define DIM(x) ( sizeof((x)) / sizeof((x)[0]) )
  這個宏不只能夠用來解決Unicode的字符串長度的問題,也能夠用在編譯時定義的表格上,它能夠得到表格的項數,以下:
class Whatever { ... };
Whatever data[] = {
   { ... },
    ...
   { ... },
};
for(int i = 0; i < DIM(data); i++) // 掃描表格尋找匹配項。
  這裏要提醒你的就是必定要注意那些在參數中須要真實字節數的API函數調用,若是你傳遞字符個數給它,它將不能正常工做。以下:
TCHAR data[20];
lstrcpyn(data, longstring, sizeof(data) - 1); // WRONG!
lstrcpyn(data, longstring, DIM(data) - 1); // RIGHT
WriteFile(f, data, DIM(data), &bytesWritten, NULL); // WRONG!
WriteFile(f, data, sizeof(data), &bytesWritten, NULL); // RIGHT
形成以上緣由是由於lstrcpyn須要一個字符個數做爲參數,可是WriteFile卻須要字節數做爲參數。
一樣須要注意的是有時候須要寫出數據的全部內容。若是你僅僅只想寫出數據的真實長度,你可能會認爲你應該這樣作:
WriteFile(f, data, lstrlen(data), &bytesWritten, NULL); // WRONG
可是在Unicode環境下,它不會正常工做。正確的作法應該是這樣:
WriteFile(f, data, lstrlen(data) * sizeof(TCHAR), &bytesWritten, NULL); // RIGHT
  由於WriteFile須要的是一個以字節爲單位的長度。(可能有些人會想「在非Unicode的環境下運行這行代碼,就意味着老是在作一個多餘的乘1操做,這樣不會下降程序的效率嗎?」這種想法是多餘的,你必需要了解編譯器實際上作了什麼,沒有哪個C或C++編譯器會把這種無聊的乘1操做留在代碼中。在Unicode環境下運行的時候,你也沒必要擔憂那個乘2操做會下降程序的效率,記住,這只是一個左移一位的操做而已,編譯器也很樂意爲你作這種替換。)
  使用_T宏並非意味着你已經建立了一個Unicode的程序,你只是建立了一個有Unicode意識的程序而已。若是你在默認的8-bit模式下編譯你的程序的話,獲得的將是一個普通的8-bit的應用程序(這裏的8-bit指的只是8位的字符編碼,並非指8位的計算機系統);當你在Unicode環境下編譯你的程序時,你纔會獲得一個Unicode的程序。記住,CString 在 Unicode 環境下,裏面包含的可都是16位的字符哦。程序員

三、CString 型轉化成 int 型

  把 CString 類型的數據轉化成整數類型最簡單的方法就是使用標準的字符串到整數轉換例程。
  雖然一般你懷疑使用_atoi()函數是一個好的選擇,它也不多會是一個正確的選擇。若是你準備使用 Unicode 字符,你應該用_ttoi(),它在 ANSI 編碼系統中被編譯成_atoi(),而在 Unicode 編碼系統中編譯成_wtoi()。你也能夠考慮使用_tcstoul()或者_tcstol(),它們都能把字符串轉化成任意進制的長整數(如二進制、八進制、十進制或十六進制),不一樣點在於前者轉化後的數據是無符號的(unsigned),然後者相反。看下面的例子:
CString hex = _T("FAB");
CString decimal = _T("4011");
ASSERT(_tcstoul(hex, 0, 16) == _ttoi(decimal));
 四、CString 型和 char* 類型的相互轉化

  這是初學者使用 CString 時最多見的問題。有了 C++ 的幫助,不少問題你不須要深刻的去考慮它,直接拿來用就好了,可是若是你不能深刻了解它的運行機制,又會有不少問題讓你迷惑,特別是有些看起來沒有問題的代碼,卻恰恰不能正常工做。
好比,你會奇怪爲何不能寫向下面這樣的代碼呢:
CString graycat = "Gray" + "Cat";
或者這樣:
CString graycat("Gray" + "Cat");
  事實上,編譯器將抱怨上面的這些嘗試。爲何呢?由於針對CString 和 LPCTSTR數據類型的各類各樣的組合,「 +」 運算符 被定義成一個重載操做符。而不是兩個 LPCTSTR 數據類型,它是底層數據類型。你不能對基本數據(如 int、char 或者 char*)類型重載 C++ 的運算符。你能夠象下面這樣作:
CString graycat = CString("Gray") + CString("Cat");
或者這樣:
CString graycat = CString("Gray") + "Cat";
研究一番就會發現:「 +」老是使用在至少有一個 CString 對象和一個 LPCSTR 的場合。

注意,編寫有 Unicode 意識的代碼老是一件好事,好比:
CString graycat = CString(_T("Gray")) + _T("Cat");
這將使得你的代碼能夠直接移植。

char* 轉化爲 CString

  如今你有一個 char* 類型的數據,或者說一個字符串。怎麼樣建立 CString 對象呢?這裏有一些例子:
char * p = "This is a test";
或者象下面這樣更具備 Unicode 意識:
TCHAR * p = _T("This is a test")

LPTSTR p = _T("This is a test");
你可使用下面任意一種寫法:
CString s = "This is a test"; // 8-bit only
CString s = _T("This is a test"); // Unicode-aware
CString s("This is a test"); // 8-bit only
CString s(_T("This is a test")); // Unicode-aware
CString s = p;
CString s(p);
  用這些方法能夠輕鬆將常量字符串或指針轉換成 CString。須要注意的是,字符的賦值老是被拷貝到 CString 對象中去的,因此你能夠象下面這樣操做:
TCHAR * p = _T("Gray");
CString s(p);
p = _T("Cat");
s += p;
結果字符串確定是「GrayCat」。

CString 類還有幾個其它的構造函數,可是這裏咱們不考慮它,若是你有興趣能夠本身查看相關文檔。

事實上,CString 類的構造函數比我展現的要複雜,好比:
CString s = "This is a test"; 
  這是很草率的編碼,可是實際上它在 Unicode 環境下能編譯經過。它在運行時調用構造函數的 MultiByteToWideChar 操做將 8 位字符串轉換成 16 位字符串。無論怎樣,若是 char * 指針是網絡上傳輸的 8 位數據,這種轉換是頗有用的。數組

 

CString 轉化成 char* 之一:強制類型轉換爲 LPCTSTR;

  這是一種略微硬性的轉換,有關「正確」的作法,人們在認識上還存在許多混亂,正確的使用方法有不少,但錯誤的使用方法可能與正確的使用方法同樣多。
  咱們首先要了解 CString 是一種很特殊的 C++ 對象,它裏面包含了三個值:一個指向某個數據緩衝區的指針、一個是該緩衝中有效的字符記數以及一個緩衝區長度。 有效字符數的大小能夠是從0到該緩衝最大長度值減1之間的任何數(由於字符串結尾有一個NULL字符)。字符記數和緩衝區長度被巧妙隱藏。
  除非你作一些特殊的操做,不然你不可能知道給CString對象分配的緩衝區的長度。這樣,即便你得到了該0緩衝的地址,你也沒法更改其中的內容,不能截短字符串,也 絕對沒有辦法加長它的內容,不然第一時間就會看到溢出。
  LPCTSTR 操做符(或者更明確地說就是 TCHAR * 操做符)在 CString 類中被重載了,該操做符的定義是返回緩衝區的地址,所以,若是你須要一個指向 CString 的 字符串指針的話,能夠這樣作:
CString s("GrayCat");
LPCTSTR p = s;
  它能夠正確地運行。這是由C語言的強制類型轉化規則實現的。當須要強制類型轉化時,C++規測允許這種選擇。好比,你能夠將(浮點數)定義爲將某個複數 (有一對浮點數)進行強制類型轉換後只返回該複數的第一個浮點數(也就是其實部)。能夠象下面這樣:
Complex c(1.2f, 4.8f);
float realpart = c;
若是(float)操做符定義正確的話,那麼實部的的值應該是1.2。
  這種強制轉化適合全部這種狀況,例如,任何帶有 LPCTSTR 類型參數的函數都會強制執行這種轉換。 因而,你可能有這樣一個函數(也許在某個你買來的DLL中):
BOOL DoSomethingCool(LPCTSTR s);
你象下面這樣調用它:
CString file("c:\\myfiles\\coolstuff")
BOOL result = DoSomethingCool(file);
  它能正確運行。由於 DoSomethingCool 函數已經說明了須要一個 LPCTSTR 類型的參數,所以 LPCTSTR 被應用於該參數,在 MFC 中就是返回的串地址。

若是你要格式化字符串怎麼辦呢?
CString graycat("GrayCat");
CString s;
s.Format("Mew! I love %s", graycat);
  注意因爲在可變參數列表中的值(在函數說明中是以「...」表示的)並無隱含一個強制類型轉換操做符。你會獲得什麼結果呢?
  一個使人驚訝的結果,咱們獲得的實際結果串是:
"Mew! I love GrayCat"。
  由於 MFC 的設計者們在設計 CString 數據類型時很是當心, CString 類型表達式求值後指向了字符串,因此這裏看不到任何象 Format 或 sprintf 中的強制類型轉換,你仍然能夠獲得正確的行爲。描述 CString 的附加數據實際上在 CString 名義地址以後。
  有一件事情你是不能作的,那就是修改字符串。好比,你可能會嘗試用「,」代替「.」(不要作這樣的,若是你在意國際化問題,你應該使用十進制轉換的 National Language Support 特性,),下面是個簡單的例子:
CString v("1.00"); // 貨幣金額,兩位小數
LPCTSTR p = v;
p[lstrlen(p) - 3] = '','';
  這時編譯器會報錯,由於你賦值了一個常量串。若是你作以下嘗試,編譯器也會錯:
strcat(p, "each");
  由於 strcat 的第一個參數應該是 LPTSTR 類型的數據,而你卻給了一個 LPCTSTR。

  不要試圖鑽這個錯誤消息的牛角尖,這隻會使你本身陷入麻煩!

  緣由是緩衝有一個計數,它是不可存取的(它位於 CString 地址之下的一個隱藏區域),若是你改變這個串,緩衝中的字符計數不會反映所作的修改。此外,若是字符串長度剛好是該字符串物理限制的長度(梢後還會講到這個問題),那麼擴展該字符串將改寫緩衝之外的任何數據,那是你無權進行寫操做的內存(不對嗎?),你會毀換壞不屬於你的內存。這是應用程序真正的死亡處方。
CString轉化成char* 之二:使用 CString 對象的 GetBuffer 方法;

  若是你須要修改 CString 中的內容,它有一個特殊的方法可使用,那就是 GetBuffer,它的做用是返回一個可寫的緩衝指針。 若是你只是打算修改字符或者截短字符串,你徹底能夠這樣作:
CString s(_T("File.ext"));
LPTSTR p = s.GetBuffer();
LPTSTR dot = strchr(p, ''.''); // OK, should have used s.Find...
if(p != NULL)
*p = _T(''\0'');
s.ReleaseBuffer();
  這是 GetBuffer 的第一種用法,也是最簡單的一種,不用給它傳遞參數,它使用默認值 0,意思是:「給我這個字符串的指針,我保證不加長它」。當你調用 ReleaseBuffer 時,字符串的實際長度會被從新計算,而後存入 CString 對象中。
  必須強調一點,在 GetBuffer 和 ReleaseBuffer 之間這個範圍,必定不能使用你要操做的這個緩衝的 CString 對象的任何方法。由於 ReleaseBuffer 被調用以前,該 CString 對象的完整性得不到保障。研究如下代碼:
CString s(...);

LPTSTR p = s.GetBuffer();

//... 這個指針 p 發生了不少事情

int n = s.GetLength(); // 很糟D!!!!! 有可能給出錯誤的答案!!!

s.TrimRight(); // 很糟!!!!! 不能保證能正常工做!!!!

s.ReleaseBuffer(); // 如今應該 OK

int m = s.GetLength(); // 這個結果能夠保證是正確的。

s.TrimRight(); // 將正常工做。
  假設你想增長字符串的長度,你首先要知道這個字符串可能會有多長,比如是聲明字符串數組的時候用:
char buffer[1024];
表示 1024 個字符空間足以讓你作任何想作得事情。在 CString 中與之意義相等的表示法:
LPTSTR p = s.GetBuffer(1024);
  調用這個函數後,你不只得到了字符串緩衝區的指針,並且同時還得到了長度至少爲 1024 個字符的空間(注意,我說的是「字符」,而不是「字節」,由於 CString 是以隱含方式感知 Unicode 的)。
  同時,還應該注意的是,若是你有一個常量串指針,這個串自己的值被存儲在只讀內存中,若是試圖存儲它,即便你已經調用了 GetBuffer ,並得到一個只讀內存的指針,存入操做會失敗,並報告存取錯誤。我沒有在 CString 上證實這一點,但我看到過大把的 C 程序員常常犯這個錯誤。
  C 程序員有一個通病是分配一個固定長度的緩衝,對它進行 sprintf 操做,而後將它賦值給一個 CString:
char buffer[256];
sprintf(buffer, "%......", args, ...); // ... 部分省略許多細節
CString s = buffer;
雖然更好的形式能夠這麼作:
CString s;
s.Format(_T("%...."), args, ...);
若是你的字符串長度萬一超過 256 個字符的時候,不會破壞堆棧。

  另一個常見的錯誤是:既然固定大小的內存不工做,那麼就採用動態分配字節,這種作法弊端更大:
int len = lstrlen(parm1) + 13  lstrlen(parm2) + 10 + 100;

char * buffer = new char[len];

sprintf(buffer, "%s is equal to %s, valid data", parm1, parm2);

CString s = buffer;

......

delete [] buffer;
它能夠能被簡單地寫成:
CString s;

s.Format(_T("%s is equal to %s, valid data"), parm1, parm2);
  須要注意 sprintf 例子都不是 Unicode 就緒的,儘管你可使用 tsprintf 以及用 _T() 來包圍格式化字符串,可是基本 思路仍然是在走彎路,這這樣很容易出錯。網絡

相關文章
相關標籤/搜索