http://video.ee.ntu.edu.tw/~howard/bcb/index.html
在C語言中,字串的處理一直是耐人尋味的,那個空結束字元(null-terminated char)一直都是個藝術品,不過也令人頭痛。下面三種型態是常用的字串使用法:
char aString[100] ; // 一個字元陣列,100個字元大小。
char *aString ; // 指向字元型態的指標
LPSTR aString ; // 同上,常用在Windows程式上 在C語言中,字串是靠空結束字元(null-terminated char)'\0',來界定的。一個沒有'\0'的字元陣列,是不能當成一個字串來看的。同理,字元指標所指到的字串,在結束的地方也必須要有一個結束字元,否則會一直往下找到結束字元出現為止。字串處理,變成了一件麻煩事,因為結束字元常常是程式問題的所在。實在不敢想像,把一個沒有結束字元的指標當成字串來處理,那會是什麼後果。 在C語言的標準函數庫(standard library)裡,有一籮筐處理字串的函數,例如strlen()、strcpy()、strcmp()、strchr()等,它們的原型宣告放在
這個標頭檔。它們也都是使用字元指標做為它們的參數,萬一使用者忽略了結束字元的處理,後果可想而知了。有沒有更好的辦法?答案是有的,那就是AnsiString這個類別。 AnsiString是C Builder從Object Pascal(Delphi使用的母語)抄過來的類別,專門處理在VCL中所有字串的處理,原因是好用的不得了。事實上,C 的類別庫裡也有專門處理字串的類別─String類別,不過為了VCL相容性考慮,C Builder還是採用了與Object Pascal相容的AnsiString類別。 使用AnsiString類別 AnsiString的原型宣告是放在dstring.h這個標頭檔,我們可以在..\Include\VCL\這個目錄找到dstring.h。你應該花點時間研究一下這個檔案的內容,這樣會對AnsiString的了解很有幫助。 AnsiString的建構函數 AnsiString封裝了一組十分完整的建構函數,底下是這些建構函數的原型:
__fastcall AnsiString() : Data(0) { }
__fastcall AnsiString(const char* src) ;
__fastcall AnsiString(const AnsiString& src) ;
__fastcall AnsiString(const char* src, unsigned char len) ;
__fastcall AnsiString(const wchar_t* src) ;
__fastcall AnsiString(char src) ;
__fastcall AnsiString(int src) ;
__fastcall Anstring(double src) ;
我們可以看到倒數兩個建構函數,把整數和浮點數(double)也封裝進來,表示我們可以直接把數值資料轉成字串,例如下面這一段程式碼:
// ……
int nNum = 543 ;
AnsiString sNum(nNum) ;
AnsiString sConstStr(“ sNum is a real string : ”) ;
cout << sConstStr.c_str() << sNum.c_str() << endl ;
// ……
程式的第二行,讀入整數資料並以字串型態存在sNum這個AnsiString物件中,接著則是一個字串常數的讀取。c_str()是AnsiString()的一個成員函數,傳回值是一個字元指標,也就是傳統的C字串,底下是c_str()函數原型:
char* __fastcall c_str( ) const ;
在C Builder中,VCL所用的字串型態都是AnsiString,所以通常初始化一個AnsiString的物件之後,就可以直接使用,比方說: //…
AnsiString sCaption(“Hello, C Builder”) ;
Form1->Caption = sCaption ;
//… AnsiString的指定運算(Assignment) 為了增加處理的能力,這個類別也把指定運算重新打造過了(overloading),下面是指定運算的函數原型:
AnsiString& __fastcall operator =(const AnsiString& rhs) ;
AnsiString& __fastcall operator =(const AnsiString& rhs) ;
指定運算現在可強悍多了,這當然是與建構函數通力合作的成果。
//…
Form1->Caption = 123 ; // 直接把數值資料指定給AnsiString
Form1->Caption = “木頭人” ; // Caption現在變成了“123木頭人”
//…
AnsiString的比較運算子(Comparison Operators) 常常我們會用到字串的比對,比方說密碼比對、身分證號碼比對等等,另外我們可能也會需要字串比較運算來做資料的排序,AnsiString也有一組比較運算子,幫你簡單地完成字串比較。底下是AnsiString比較運算子的函數原型: bool __fastcall operator ==(const AnsiString& rhs) const;
bool __fastcall operator !=(const AnsiString& rhs) const;
bool __fastcall operator <(const AnsiString& rhs) const;
bool __fastcall operator >(const AnsiString& rhs) const;
bool __fastcall operator <=(const AnsiString& rhs) const;
bool __fastcall operator >=(const AnsiString& rhs) const;
int __fastcall AnsiCompare(const AnsiString& rhs) const;
int __fastcall AnsiCompareIC(const AnsiString& rhs) const; //ignorecase
字串大小的判定是由英文字母順序來決定的,例如:"apple"會大於"orange",而"cat"會大於"cow"。如此一來,字串的比對就再簡單不過了。 //…
const char* password = “C Builder is Excellent”;
cout << “Hello! Please input password” << endl ;
AnsiString inPassword ;
cin >> inPassword ;
if (inPassword == password)
cout << “WelCome to C Builder” << endl ;
else
{
cout << “You are not allowed to enter” << endl ;
exit(0) ;
}
//…
另外,字串的排序也可以很容易來完成。 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
// List2.2 sort.cpp
//---------------------------------------------------------------------------
#include
#include
#include
#include
#include
#include #pragma hdrstop
//---------------------------------------------------------------------------
USERES("sort.res");
//---------------------------------------------------------------------------
int main(int argc, char **argv)
{
const int NUM = 10 ;
const char* fruit[NUM] = { "howard", "joe", "bach", "pony", "betty", "cth", "syma", "sor", "han", "lsf" } ;
AnsiString sorted[NUM] ; // initialize
for (int index=0 ; index 我們使用了一個很簡單的排序演算法─Selection Sort來排列字串的順序,當然你可以使用更有效率的排序演算法,如Quict Sort,不過,這並不是我們討論的重點。我們可以看到,AnsiString類別把比較運算子重新overloading,讓我們在字串的比對、比較的用途上更加的直覺與方便。感受到它的威力了嗎?你還可以在CDROM上,找到更多的相關資料與範例喔! AnsiString的其他成員函數 在C Builder的線上輔助說明(On Line Help),你可以找到更詳細的成員函數的說明,如果你願意而且也有興趣的話,也可以直接去研究在 ..\Source\VCL\這個目錄下的dstring.cpp,這是整個AnsiString類別的實作。不過,基於物件封裝性,你大可以不必理會AnsiString類別是如何實作的,只要把成員函數掌握的一清二楚就夠了。就算你想要自己打造一個處理字串的類別,那也別忘了繼承這個法寶,藉由繼承AnsiString類別,你也可以做出一個具有個人風味而且功能強大的string類別。 底下列出了一些常見的AnsiString成員函數: char* __fastcall c_str() const ;
int __fastcall Length() const ;
friend AsiString __fastcall operator (const char* lhs, const AnsiString& rhs) ;
AnsiString __fastcall operator (const AnsiString& rhs) const ;
int __fastcall Pos(const AnsiString& subStr) const ;
void __fastcall SetLength(int newLength);
static AnsiString __fastcall StringOfChar(char ch, int count) ;
AnsiString __fastcall SubString(int index, int count) const;
AnsiString __fastcall UpperCase() const ;
AnsiString __fastcall LowerCase() const ;
bool __fastcall IsDelimiter(const AnsiString& delimiters, int index) const;
int __fastcall LastDelimiter(const AnsiString& delimiters) const;
上面的這幾個成員函數都是與字串處理相關的,比方說c_str()就是把AnsiString物件的字串成員轉成一般的字元指標輸出,又譬如兩個多載後的” ”號運算子,可以把兩個字串連接起來;另外Length()可以把字串的長度讀出;SetLength() 可以重新設定字串長度,也就是說切掉字串的片斷;而SubString()則可以取出字串的部份出來,形成一個新的字串;UpperCase()、LowerCase()則是對字串轉換大小寫。底下這個範例,在一般需要檔案處理的地方,很常會用得到,主要是用來分離出一個檔案的路徑名稱(PathName)與檔案名稱(FileName): ///////////////////////////////////////////////////////////////////////////////////////////////
/ List2.3 path1.cpp
//---------------------------------------------------------------------------
#include
#include
#include
#include
#include
#include #pragma hdrstop
//---------------------------------------------------------------------------
USERES("path1.res");
//---------------------------------------------------------------------------
int main(int argc, char **argv)
{
AnsiString FullName = "C:\\Program Files\\Projects\\Cbuilder\\myprog.cpp" ;
int bkPos = FullName.LastDelimiter("\\") ;
int strLength = FullName.Length() ;
AnsiString PathName = FullName.SubString(0, bkPos) ;
AnsiString FileName = FullName.SubString(bkPos 1, strLength-bkPos) ;
AnsiString desFullName = PathName FileName ;
cout << PathName.c_str() << endl ;
cout << FileName.c_str() << endl ;
cout << FullName.c_str() << endl ;
cout << desFullName.c_str() << endl ; getch() ;
return 0;
}
//---------------------------------------------------------------------------
這個程式片段的執行結果如下: C:\Program Files\Projects\Cbuilder\
myprog.cpp
C:\Program Files\Projects\Cbuilder\myprog.cpp
C:\Program Files\Projects\Cbuilder\myprog.cpp
如果你考慮程式的效率,也可以使用傳統的C字串處理方式: ////////////////////////////////////////////////////////////////////////////////////////////////////////
// List2.3 Path2.cpp
////////////////////////////////////////////////////////////////////////////////////////////////////////
#include
#include
#include int main()
{ const char *fullname = "C:\\Program Files\\Projects\\Cbuilder\\myprog.cpp" ;
char pathname[80], filename[20] ;
int i, len;
len = strlen(fullname);
for(i = len - 1; i >= 0; i--)
{
if(fullname[i] == '\\') break;
} strncpy(pathname, fullname, i 1);
pathname[i 1] = 0;
strcpy(filename, fullname i 1)
cout << "FullName--> " << fullname << endl ;
cout << "PathName--> " << pathname << endl ;
cout << "FileName--> " << filename << endl ; getch() ;
return 0 ;
}
時間就是金錢---[ 發問前請先找找舊文章]