分享

点睛之笔 —— 为 C++ Builder 加 Split 的说

 quasiceo 2014-01-02
为 C++ Builder 加 Split 的说


声明

个人可以自由转载本文,不过应保持原文的完整性,并通知我;商业转载先请和我联系。

本文没有任何明确或不明确地提示说本文完全正确,阅读和使用本文的内容是您自己的选择,本人不负任何责任,但是如果您发现本文有错漏的地方,希望您可以给我指出。

本文假定您已经对编程比较熟悉,所以对一些本人认为简单的问题不会做太多的解释,如果有什么问题,可以给我提出。(如果您知道在 C++ Builder 中有现成的 Split 函数,请务必通知我,谢谢)

意见、建议和提出的问题最好写在我的主页 http://llf.126.com 的留言版上。

无所不在的 Split

最近,对 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 类,想必真的会被它给活活气死的! :)

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多