3.vector實現字符串類

  3.vector實現字符串類

  本章前言:

    身爲土生土長的中國程序員,你肯定要用unicode來編寫程序。但是由wchar_t數組實現的字符串準確點說應該叫字符數組,但它在使用時比較繁瑣,而且容易出現數組越界和字符串結尾不爲0的錯誤。 爲了方便實現字符串相關的功能,DND引擎抽象了String類,其底層是vector<wchar_t>。爲什麼不使用std::string呢,因爲其不能定製化功能,例如+操作符直接連接兩個字符串、使用sprintf格式化生成字符串等等。

  目標要點總結:

1.  實現字符串類

2.  類似基礎類型的使用方式

  最終效果:

    例如下面這樣使用:

    Stringstr=String(L”Hello”)+ L” ” + L”DND!”;//str等於Hello DND!

    具體的接口可以查看頭文件中的註釋。

    class StrVector;

    class DLL_APIString

    {

    public:

        //==================構造、析構、操作符========================

        String();//空串

        String(constchar* str);//字符數組

        String(constwchar_t* wcs);//寬字符數組

        String(wchar_twc);//寬字符

        ~String();//析構

        String(constString& b);//複製構造

        //String(const unsigned b);

        String(constint b);//數字構造

        String(wchar_tch, unsigned len);//填充len ch

        String(StrVector*);//strvector構造

        String& operator=(constString& b);//=號重載

        String operator+(constString& b) const;//連接

        bool operator==(constString& b) const;//相同

        bool operator<(constString& b) const;//小於

 

        unsigned Get_Length() const;//返回長度

        //==================轉化==================

        const wchar_t*Get_Wcs() const;

        void Get_Wide_Char_Str(wchar_t* target, unsigned max_len) const;//獲得寬字符數組

        void Get_Multi_Byte_Str(char* target, unsigned max_len) const;//獲取字符數組

        int Get_Int();//返回int

 

        void Clear();//設爲空串

        void Pop();//去掉結尾

       

        //==================查找==================

        unsigned Find_End(wchar_t wc);//查找最後一個

        unsigned Find_Str(const String&str);//查找字符串位置,返回-1代表不存在

        unsigned Find_N(wchar_t wc,unsigned N);//查找第n個要查找的字符

        unsigned Get_Char_Count(wchar_t ch);//返回某字符出現的個數

        String Get_Str(unsigned begin, unsigned end);//返回區間內的字符串

        //==================刪除==================

        void Cut(unsigned begin,unsigned end);//去掉區間內的字符串 [b, e]

        void Cut_Tail(unsigned i);//去掉i位置後的包括i

        void Cut_Head(unsigned i);//去掉i位置前的包括i

        void Cut_Head_Str(const String& str);//去除頭部字符串

       

       

        //==================修改==================

        void Delete_Char(unsigned i);//i位置刪除一個字符

        void Insert_Char(unsigned i, wchar_t ch);//i位置前插入一個字符,第一個字符位置爲0

        void Replace_Char(wchar_t source, wchar_t target);//替換某個字符

       

        unsigned Split(wchar_t wc,String* strs, unsigned max_size);//返回實際分隔後的字符串個數。例如 a;b;返回 a b 2

        static StringFormat(unsignedmax_size, const wchar_t* format, ...);//max size不包含結束符

    private:

        StrVector* p;

        void _init();

        void _copy(const wchar_t*wcs);

    }; 

   

前題簡要:

    無。

具體實現:

    由於需要生成動態鏈接庫,所以要在導出的接口中隱藏掉vector<wchar_t>類,可以使用void指針保存指向實際的vector<wchar_t>對象的地址,然後在接口定義中進行指針強轉。但這樣代碼看上去過於雜亂,有更好的方式實現。在前面的頭文件可以看到StrVector類的聲明:

    class StrVector;

    這樣就能在String類中聲明StrVector指針類型的變量p。在cpp中才實現了StrVector的定義,StrVector的定義如下:

    class StrVector :public vector<wchar_t>

    {

    public:

        StrVector(vector<wchar_t>*p) :

            vector<wchar_t>(*p){}

        StrVector(){}

    };

    這樣String類的指針p就能直接訪問基類vector<wchar_t>的成員。p指針的初始化交給String類的構造函數,構造函數會調用_init()。

    void String::_init()

    {

        p = newStrVector;

    }

    例如默認構造函數會構造一個空串,如下:

    String::String()

    {

        _init();

    }

    使用char字符數組構造:

    String::String(constchar* str)

    {

        _init();//初始化

 

        unsigned len =strlen(str) + 1;//字符串長度

        wchar_t* wcs =new wchar_t[len];//分配臨時內存

        //multi_bytewide_char(即char數組 wchar_t

        MultiByteToWideChar(CP_ACP,NULL, str, -1, wcs, len);

        //設置vector最大長度爲len

        p->reserve(len);

        //複製字符串

        _copy(wcs);

        delete[] wcs;

    }

    其中首先獲取字符串長度,然後分配足夠的內存大小(加1放下結束符)。然後調用MultiByteToWideChar函數將char數組轉換至wchar_t數組,再預先設置vector<wchar_t>的最大長度。調用_copy將數據從buffer複製到vector,最後釋放buffer。

    連接字符串的實現如下,首先複製構造一個臨時對象str,然後在尾部插入b字符的所有字符,最後再返回拷貝:

    String String::operator+(constString& b) const

    {

        String str = *this;

        str.p->insert(str.p->end(),b.p->begin(),b.p->end());

        return str;

    }

    有很多系統函數、庫函數都要求以const wchar_t*類型作爲函數參數,讓String快速的返回字符串首地址很有必要。由於String的vector不包含結束符,所以需要擴容尾部爲0,但長度仍然不變。實際的實現如下:

    const wchar_t*String::Get_Wcs()const

    {

        p->push_back(0);

        wchar_t * temp = &(*p)[0];//保證0下標不越界

        p->pop_back();

        return temp;

    }

    比如在排序兩個字符串的時候就用到了Get_Wcs,如下:

    bool String::operator<(constString& b) const

    {

        return wcscmp(Get_Wcs(),b.Get_Wcs()) < 0;

    }

    sprintf函數用來格式化字符串非常好用,所以有必要封裝類似的接口,封裝之後可以這樣生成一個字符串:

    String str =String::Format(256,L"FPS:%d dt:%f", fps, dt);

    Format是一個靜態變長參數函數,使用方式類似於sprintf,第一個256是指臨時buffer的最大長度,具體實現如下:

    String String::Format(unsignedmax_size, const wchar_t* format, ...)

    {

        wchar_t* wcs =new wchar_t[max_size + 1];

        va_list args;

        va_start(args,format);

 

        vswprintf_s(wcs,max_size + 1, format,args);

 

        va_end(args);

 

        String ret =String(wcs);

        delete[] wcs;

        return ret;

    }

    有關查找、刪除、編輯功能的實現這裏就不貼了,很簡單,使用vector的接口很好實現。作者以前實現的String是基於wchar_t數組的,由自己慢慢的添加接口而成(手動寫每個算法真的很麻煩)。最近規範下了命名,並換用了vector<wchar_t>實現,一天就完成了,所以標準庫還是好用啊!

 

作者:略遊

日期:17-06-20

QQ:1339484752

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