3.vector实现字符串类

时间:2022-01-01 04:17:00

  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(constintb);//数字构造

        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,unsignedN);//查找第n个要查找的字符

        unsigned Get_Char_Count(wchar_t ch);//返回某字符出现的个数

        String Get_Str(unsigned begin, unsigned end);//返回区间内的字符串

        //==================删除==================

        void Cut(unsigned begin,unsignedend);//去掉区间内的字符串 [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 :publicvector<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