个人可以自由转载本文,不过应保持原文的完整性,并通知我;商业转载先请和我联系。 本文没有任何明确或不明确地提示说本文完全正确,阅读和使用本文的内容是您自己的选择,本人不负任何责任,但是如果您发现本文有错漏的地方,希望您可以给我指出。 本文假定您已经对编程比较熟悉,所以对一些本人认为简单的问题不会做太多的解释,如果有什么问题,可以给我提出。(如果您知道在 C++ Builder 中有现成的 Split 函数,请务必通知我,谢谢) 意见、建议和提出的问题最好写在我的主页 http://llf.126.com 的留言版上。
最近,对 C++ Builder 比较感兴趣了。因为它是 C ,而 C 的移植性会好一些,如果把在 Windows 平台上开发的程序移植到 Unix 上去的话,C 就比 VB 或者 Delphi 更具优势。当然,C++ Builder 是否还是纯种的 C++ 不太好说,特别它还能同时编译 Delphi 的程序,好是好,不过也让人相信,我们使用的很多类其实是继承自 Delphi 的源代码的,这样的话,移植性又仍然不好了,不过不说了,因为在使用中发现 C++ Builder 现有的类非常不好用,我想我大概会自己编写一套“纯种”的 C++ 类库,这样的话就应该不存在兼容性的问题了。 不说这些了,还是回到程序上来。有些习惯真的很难改掉,比如我在处理字符串的时候,每每都会希望使用 Split 、Join 这些函数,特别是 Split 函数,缺了它简直就如同断了左膀右臂,干什么都不顺手,所以我的第一个目标就是在 C++ Builder 中实现 Split 函数。 要实现 Split 函数,第一个问题和 VB 碰到的一样,那就是 C++ Builder 能返回字符串数组吗?当然,这里说的字符串数组不能单纯的是一个指针,因为也要从其中得到数组大小的数据,但是又不想多加一个参数,我们知道,C++ 确实可以返回对象指针,那么现在的问题就是:C++ Builder 中有现成的字符串数组类吗? 参考了一些 Delphi 的教程,发现 Delphi 里是有的,叫做 TStrings ,但是我在 C++ Builder 中使用的时候,编译器告诉我 TStrings 是一个抽象类,不能直接使用,真是可恶,不明白为什么 TStrings 就是一个抽象类,平白地浪费了这么好的名字。再搜查,找到了一个 TStringList 类,这是可以的,所以第一个问题解决了。 第二个问题是 C++ Builder 中的字符串类。察看了一下帮助,字符串类共有三种:ShortString 、AnsiString 、WideString 。ShortString 只能到 255 字节,故不取,而后两种 AnsiString 表示 ANSI 字符串,WideString 表示 UniCode 字串(关于 ANSI 字符串和 UniCode 字串的问题可参见我的另一篇文章《VB 与 UniCode》),大家如果熟悉 UniCode 的话,想必会和我一样选择 WideString 的。 至此,应该没有问题了,不过 WideString 又给我开了一个不大不小的玩笑:可以使用其成员函数 Pos 取得另一个字串在当前字串中的位置,但是却不能设定从哪里开始查起!于是我不得不在查到一个匹配之后就把原字串重新使用 SubString 赋值一次,实在是可恶之极了,我想这一次在 C++ Builder 上实现的 Split 一定会比 VB 中的慢了,不过想一想,大概也不会用它处理太大的字串,问题倒也不大。 TStringList *Split(WideString Spliter, WideString String, int SplitNumber = 0) { int const MaxPos = 0x7fffffff, StartPos = 1; int FindPos, CharLen, Length, MySplitNumber=0; TStringList *Strs = new TStringList; BOOL LostTail; //if (SplitNumber == 0) LostTail = TRUE; 不严格,LostTail 在 // SplitNumber != 0 时可能是任何值。故修改如下: LostTail = (SplitNumber == 0); //^^^^^^^^^^^^^^^^^^^^^^^^^^\\ if (SplitNumber <= 0) SplitNumber = MaxPos; else MySplitNumber = SplitNumber; CharLen = Spliter.Length(); Length = String.Length(); while(1) { FindPos = String.Pos(Spliter); if (CharLen <= 0){ if (FindPos > Length) break; else FindPos++; } if ((FindPos == 0) || (SplitNumber <= 1)){ Strs->Add(String.SubString(StartPos,MaxPos)); break; } --SplitNumber; Strs->Add(String.SubString(StartPos,FindPos - StartPos)); String = String.SubString(FindPos+CharLen,MaxPos); } int i; for (i=Strs->Count; i<MySplitNumber; i++){ Strs->Add(""); } if (LostTail){ for (i = Strs->Count - 1; i>=0; i--) if (Strs->Strings[i] == "") Strs->Delete(i); else break; } return Strs; } 实现本身就没有什么可说的了,几乎照搬我在 VB 中的实现,不过调用还是需要小心一些: //--------------------------------------------------------------------------- #define MsgBox(Text) MessageBox(this->Handle,Text,this->Caption.c_str(),MB_OK); #include "Perl2.cpp" //--------------------------------------------------------------------------- void __fastcall TForm1::Button1Click(TObject *Sender) { char Str4Split[] = "abc,123,uio,,,,"; TStringList *Strs; Strs = Split(",", Str4Split,-1); for (int i=0; i < Strs->Count; i++) { MsgBox(Strs->Strings[i].c_str()); } delete Strs; } //--------------------------------------------------------------------------- 如上,定义变量时要使用“TStringList *Strs”,而在函数结束的时候,一定要调用一下“delete Strs;”,据说不然会造成内存泄漏,虽然不知道为什么这样,不过小心使得万年船,听话就是了。 :-< 自我感觉,这一次实现 C++ Builder 版的 Split 真是一种痛苦,如果某一时刻我又需要使用它的 Variant 类,想必真的会被它给活活气死的! :) |
|