东莞东坑网站设计/发帖子的网站
今天我们来看看字符串类。在之前的学习中,我们得知:在 C 语言中,它是不支持真正意义上的字符串;它用字符数组和一组函数实现字符串操作;C 语言是不支持自定义类型的,因此无法获得字符串类型。那么从 C 到 C++ 的进化过程中引入了自定义类型,在 C++ 中可以通过类完成字符串类型的定义。那么问题来了:在 C++ 中的原生类型系统是否包含字符串类型呢?我们将会在本节博客中来回答这个问题。
下来我们先来看看 DTLib 库中字符串类的设计,结构如下图所示
它的具体实现如下图所示
那么我们在实现时应注意那些事项呢?
1、无缝实现 String 对象与 char* 字符串的互操作;
2、操作符重载函数需要考虑是否支持 const 版本;
3、通过 C 语言中的字符串函数实现 String 的成员函数。
String 的具体实现如下
DTString.h 源码
#ifndef DTSTRING_H
#define DTSTRING_H#include "Object.h"namespace DTLib
{class String : public Object
{
protected:char* m_str;int m_length;void init(const char* s);
public:String();String(char c);String(const char* s);String(const String& s);int length() const;const char* str() const;bool operator == (const String& s) const;bool operator == (const char* s) const;bool operator != (const String& s) const;bool operator != (const char* s) const;bool operator > (const String& s) const;bool operator > (const char* s) const;bool operator < (const String& s) const;bool operator < (const char* s) const;bool operator >= (const String& s) const;bool operator >= (const char* s) const;bool operator <= (const String& s) const;bool operator <= (const char* s) const;String operator + (const String& s) const;String operator + (const char* s) const;String& operator += (const String& s);String& operator += (const char* s);String& operator = (const String& s);String& operator = (const char* s);String& operator = (char c);~String();
};}#endif // DTSTRING_H
DTString.cpp 源码
#include <iostream>
#include <cstring>
#include <cstdlib>
#include "DTString.h"
#include "Exception.h"using namespace std;namespace DTLib
{void String::init(const char* s)
{m_str = strdup(s);if( m_str ){m_length = strlen(m_str);}else{THROW_EXCEPTION(NoEnoughMemoryException, "No memory to create String object ...");}
}String::String()
{init("");
}String::String(char c)
{char s[] = {c, '\0'};init(s);
}String::String(const char* s)
{init(s ? s : "");
}String::String(const String& s)
{init(s.m_str);
}int String::length() const
{return m_length;
}const char* String::str() const
{return m_str;
}bool String::operator == (const String& s) const
{return (strcmp(m_str, s.m_str) == 0);
}bool String::operator == (const char* s) const
{return (strcmp(m_str, s ? s : "") == 0);
}bool String::operator != (const String& s) const
{return !(*this==s);
}bool String::operator != (const char* s) const
{return !(*this==s);
}bool String::operator > (const String& s) const
{return (strcmp(m_str, s.m_str) > 0);
}bool String::operator > (const char* s) const
{return (strcmp(m_str, s ? s : "") > 0);
}bool String::operator < (const String& s) const
{return (strcmp(m_str, s.m_str) < 0);
}bool String::operator < (const char* s) const
{return (strcmp(m_str, s ? s : "") < 0);
}bool String::operator >= (const String& s) const
{return (strcmp(m_str, s.m_str) >= 0);
}bool String::operator >= (const char* s) const
{return (strcmp(m_str, s ? s : "") >= 0);
}bool String::operator <= (const String& s) const
{return (strcmp(m_str, s.m_str) <= 0);
}bool String::operator <= (const char* s) const
{return (strcmp(m_str, s ? s : "") <= 0);
}String String::operator + (const String& s) const
{return (*this + s.m_str);
}String String::operator + (const char* s) const
{String ret;int len = m_length + strlen(s ? s : "");char* str = reinterpret_cast<char*>(malloc(len + 1));if( str ){strcpy(str, m_str);strcat(str, s ? s : "");free(ret.m_str);ret.m_str = str;ret.m_length = len;}else{THROW_EXCEPTION(NoEnoughMemoryException, "No memory to add String values ...");}return ret;
}String& String::operator += (const String& s)
{return (*this = *this + s.m_str);
}String& String::operator += (const char* s)
{return (*this = *this + s);
}String& String::operator = (const String& s)
{return (*this = s.m_str);
}String& String::operator = (const char* s)
{if( m_str != s ){char* str = strdup(s ? s : "");if( str ){free(m_str);m_str = str;m_length = strlen(m_str);}else{THROW_EXCEPTION(NoEnoughMemoryException, "No memory to assign new String value ...""");}}return *this;
}String& String::operator = (char c)
{char s[] = {c, '\0'};return (*this = s);
}String::~String()
{free(m_str);
}}
我们来测试下上面的代码,测试代码如下
#include <iostream>
#include "DTString.h"using namespace std;
using namespace DTLib;void test_1()
{cout << "test_1() begin ..." << endl;String s;s = 'D';cout << "s.str() = " << s.str() << endl;cout << "s.length() = " << s.length() << endl;cout << "(s == 'D') = " << (s == 'D') << endl;cout << "(s > CCC) = " << (s > "CCC") << endl;s += " hello world";cout << "s.str() = " << s.str() << endl;cout << "s.length() = " << s.length() << endl;cout << "(s > D hello world) = " << (s == "D hello world") << endl;cout << "test_1() end ..." << endl;
}void test2()
{cout << "test_2() begin ..." << endl;String a[] = {"E", "D", "C", "B", "A"};String min = a[0];for(int i=0; i<5; i++){if( min > a[i] ){min = a[i];}}cout << "min = " << min.str() << endl;cout << "test_2() end ..." << endl;
}int main()
{test_1();test2();return 0;
}
我们先来分析下,在函数 test_1 中,我们先定义 String s = 'D'; s.str = D; s.length = 1; (s == 'D') = 1; (s > "CCC") = 1; 在下面 + " hello world" 后,它的长度就变成 13 了。在函数 test_2 后,打印最小的字符串,应该最后输出为 A。我们来看看结果
我们此时已经实现了字符串的一些基本操作。那么我们接下来会实现一些比较高级的操作,功能如下
重载数组访问操作符 [ ] :a> char& operator [] (int i); b> char operator [] (int i) const; 注意事项:当 i 的取值不合法时,抛出异常,合法的范围是 (0 <= i) && (i < m_length)。源码如下
char& String::operator [] (int i)
{if((0 <= i) && (i < m_length)){return m_str[i];}else{THROW_EXCEPTION(IndexOutOfBoundsException, "Parameter i is invalid ...");}
}char String::operator [] (int i) const
{return (const_cast<String&>(*this))[i];
}
我们来测试下,测试代码如下
#include <iostream>
#include "DTString.h"using namespace std;
using namespace DTLib;int main()
{String s = "hello world";for(int i=0; i<s.length(); i++){cout << s[i] << endl;}return 0;
}
我们来看看编译结果
下来我们来继续是实现判断是否以指定字符串开始或结束,a> bool startWith(const char* s) const; b> bool startWith(const String& s) const; c> bool endOf(const char* s) const; d> bool endOf(const String& s) const; 我们来看看具体的实现思路,如下图所示
如果是以某个字符串开头或者结束的时候,我们在前几个或者后几个字符处来进行对比,看看是否相同,从而来达到实现我们想要的功能。具体源码如下
bool String::equal(const char* l, const char* r, int len) const
{bool ret = true;for(int i=0; i<len; i++){ret = ret && (l[i] == r[i]);}return ret;
}bool String::startWith(const char* s) const
{bool ret = (s != NULL);if( ret ){int len = strlen(s);ret = (len < m_length) && equal(m_str, s, len);}return ret;
}bool String::startWith(const String& s) const
{return startWith(s.m_str);
}bool String::endOf(const char* s) const
{bool ret = (s != NULL);if( ret ){int len = strlen(s);char* str = m_str + (m_length - len);ret = (len < m_length) && equal(str, s, len);}return ret;
}bool String::endOf(const String& s) const
{return endOf(s.m_str);
}
测试代码如下
#include <iostream>#include "DTString.h"using namespace std;
using namespace DTLib;int main()
{String s = "hello world";cout << s.startWith("he") << endl;cout << s.endOf("world") << endl;return 0;
}
我们来看看编译结果
我们继续来实现剩下的功能,在指定位置处插入字符串:a> String& insert(int i, const char* s); b> String& insert(int i, const String& s); 实现思路如下
我们在某个位置处插入一个字符串,重新申请一个堆空间,然后将其插入,最后将剩下的字符串接上。最后要记着更新 m_str, m_length。具体源码实现如下
String& String::insert(int i, const char* s)
{if( (0 <= i) && (i <= m_length) ){if( (s != NULL) && (s[0] != '\0') ){int len = strlen(s);char* str = reinterpret_cast<char*>(malloc(m_length + len + 1));if( str != NULL ){strncpy(str, m_str, i);strncpy(str + i, s, len);strncpy(str + i + len, m_str + i, m_length - i);str[m_length + len] = '\0';free(m_str);m_str = str;m_length = m_length + len;}else{THROW_EXCEPTION(NoEnoughMemoryException, "No memory to insert string value ...");}}}else{THROW_EXCEPTION(INvalidOPerationException, "Parameter i is invalid ...");}return *this;
}String& String::insert(int i, const String& s)
{return insert(i, s.m_str);
}
测试代码如下
#include <iostream>
#include "DTString.h"using namespace std;
using namespace DTLib;int main()
{String s = "";s.insert(0, "hello,");s.insert(6, "world");cout << s.str() << endl;return 0;
}
我们看看编译结果
我们来实现最后一个功能,去掉字符串两端的空白字符:String& trim(); 实现思路如下
我们来分析下,在它的前后两端去除空白字符。也就是说,定义两个整数,前后遍历,直至它不等于 '\0 '。此时已经到了我们所需要的位置,再重新进行组合即可。具体源码实现如下
String& String::trim()
{int b = 0;int e = m_length -1;while( m_str[b] == ' ' ) b++;while( m_str[e] == ' ' ) e--;if( b == 0 ){m_str[e + 1] = '\0';m_length = e + 1;}else{for(int i=0, j=b; j<=e; i++, j++){m_str[i] = m_str[j];}m_str[e - b + 1] = '\0';m_length = e - b + 1;}return *this;
}
测试代码如下
#include <iostream>
#include "DTString.h"using namespace std;
using namespace DTLib;int main()
{String s = " abc ";cout << s.trim().str() << endl;if( s.trim().insert(0, "hello,").endOf("abc") && s.startWith("hello") ){cout << "[" << s.trim().str() << "]" << endl;}return 0;
}
我们在上面实现的是链式去除,因此上面的先去除再直接打印操作是可以的。我们来看看编译结果
我们直至现在已经实现了一些字符串比较高级的功能。那么在后面,我们再思考下,是否能基于现在实现的基础上,如何在目标字符串中查找是否存在指定的子串?通过今天对字符串的学习,总结如下:1、C/C++ 语言本身不支持字符串类型;2、C 语言通过字符数组和一组函数支持字符串操作;3、C++ 通过自定义字符串类型支持字符串操作;4、字符串类型通过 C 语言中的字符串函数实现。
转载于:https://blog.51cto.com/12810168/2236043