1,历史遗留问题:
1,C 语言不支持真正意义上的字符串;
2,C 语言用字符数组和一组函数实现字符串操作;
1,字符数组模拟字符串;
2,字符数组以 来结束就是合法字符串;
3,C 语言中没有单独类型支持字符串,要么是字符数组,要么是 char* 指针;
3,C 语言不支持自定义类型,因此无法获得字符串类型;
2,从 C 到 C++ 的进化过程引入了自定义类型;
在 C++ 中可以通过类完成字符串类型的定义;
C++ 中的原生类型不包含字符串类型,要兼容 C 语言;
3,C++ 中通过库来支持字符串类型:
1,stl 中就有 string 类,这是官方承认的 string 类型;
2,Qt 中提供 QString;
3,MFC 中提供 CString;
4,数据结构库中也应该包含字符串库,否则拿出去应该没人使用的,本文就是设计自己的 String 类;
5,DTLib 中字符串类的设计:
1,所以字符串类的设计方案基本一致,只不过是出自不同厂商,对应的类型不同,在对应的类型下面使用设计的方法确是一样的;
2,继承自 Object,依赖 C 语言关于字符串函数的一个包(函数集);
3,要注意设计的成员函数的先后秩序;
6,DTLib 中字符串类的实现(本博文中实现了包括KMP算法应用在内的一系列字符串功能函数,包括后续“字符串类——字符串类的创建(下)”、“字符串类———KMP子串查找算法”、“字符串类——KMP算法的应用”博文中的内容,不限于下图):
7,实现时的注意事项:
1,无缝实现 String 对象与 char* 字符串的互操作;
2,操作符重载函数需要考虑是否支持 const 版本;
3,通过 C 语言中的字符串函数实现 String 的成员函数;
8,C 语言中的字符串类型:
1,常量字符串:
1,const char* string,string 指向字符数组首地址;
2,char string[],string[] 为字符数组;
2,即要么是字符串数组本身,要么是指向字符数组首地址(一般的是这样)的 char*;
3,本质还是字符数组;
9,字符串类 String 的实现:
1,String.h 的实现:
1 #ifndef DTSTRING_H 2 #define DTSTRING_H 3 4 #include "Object.h" 5 6 namespace DTLib 7 { 8 9 class String : public Object // 库规则中每个类都要继承自顶层父类 Object 10 { 11 protected: // 面相对象的技术封装 C 语言中的字符串实现。 12 char* m_str; // 指向字符串,字符串的表现形式是字符数组; 13 int m_length; // 当前字符串的长度; 14 15 void init(const char* s); // 初始化函数 16 /* 比对函数,为 startWith() 服务,前两个参数为字符数组的首地址,第三个参数是字符数组长度;长度范围内字符数组对应元素都相等,返回真; */ 17 bool equal(const char* l, const char* r, int len) const; 18 static int* make_pmt(const char* p); 19 static int kmp(const char* s, const char* p); 20 21 public: 22 String(); 23 String(char c); 24 String(const char* s); 25 String(const String& s); // 拷贝构造函数; 26 27 int length() const; // 得到字符串长度; 28 const char* str() const; // 字符串对象与传统字符串进行互操作的转换函数 29 30 /* 判断当前的字符对象是否以 s 开头,判断当前的字符对象是否以 s 结束 */ 31 bool startWith(const char* s) const; 32 bool startWith(const String& s) const; 33 bool endOf(const char* s) const; 34 bool endOf(const String& s) const; 35 36 /* 将字符串 s 插入到对象下标为 i 处,返回 String& 是为了链式操作,返回字符串自己 */ 37 String& insert(int i, const char* s); 38 String& insert(int i, const String& s); 39 40 /* 去掉字符串中的空格 */ 41 String& trim(); 42 43 int indexOf(const char* ) const; 44 int indexOf(const String& s) const; 45 46 /* 删除字符串中的子串 s */ 47 String& remove(int i, int len); // 删除下标 i 处指定长度 len 的长度; 48 String& remove(const char* s); 49 String& remove(const String& s); 50 51 /* 用 s 替换字符串中的 t */ 52 String& replace(const char* t, const char* s); 53 String& replace(const String& t, const char* s); 54 String& replace(const char* t, const String& s); 55 String& replace(const String& t, const String& s); 56 57 /* 提取以 i 为起点去长度为 len 的子串 */ 58 String sub(int i, int len) const; // 因为这里不会改变当前字符串状态,所以为 const 成员函数; 59 60 /* 字符串对象应该能够像字符数组一样,通过每一个下标来访问每一个字符; */ 61 char& operator [] (int i); // 引用意味着可以被赋值,可以出现在赋值符号左边(此时是对象),给没有被 const 修饰的版本用; 62 char operator [] (int i) const; // 不能作为左值,所以不能返回引用对象;给 const 修饰的常对象版本使用 63 64 /* 比较操作符重载函数,兼容字符串对象与 C 语言中 const char* 所代表的字符串中的逻辑操作;封装 strcmp 函数完成 ;const 版本给被 const 修饰的常对象使用的,未被 const 修饰的对象也可以作为参数被调用;*/ 65 bool operator == (const String& s) const; 66 bool operator == (const char* s) const; 67 bool operator != (const String& s) const; 68 bool operator != (const char* s) const; 69 bool operator > (const String& s) const; 70 bool operator > (const char* s) const; 71 bool operator < (const String& s) const; 72 bool operator < (const char* s) const; 73 bool operator >= (const String& s) const; 74 bool operator >= (const char* s) const; 75 bool operator <= (const String& s) const; 76 bool operator <= (const char* s) const; 77 78 79 /* 加法操作符重载函数 */ 80 String operator + (const String& s) const; 81 String operator + (const char* s) const; 82 String& operator += (const String& s); 83 String& operator += (const char* s); 84 85 /* 减法操作符重载函数 */ 86 String operator - (const String& s) const; 87 String operator - (const char* s) const; 88 String& operator -= (const String& s); //成员会改变的,所以不能用const修饰了 89 String& operator -= (const char* s); 90 91 /* 赋值操作符重载函数 */ 92 String& operator = (const String& s); 93 String& operator = (const char* s); 94 String& operator = (char c); // 加上一个字符 95 96 ~String(); 97 }; 98 99 } 100 101 #endif // DTSTRING_H
2,String.cpp 的实现:
1 #include <cstring> 2 #include <cstdlib> 3 #include "DTString.h" 4 #include "Exception.h" 5 6 using namespace std; 7 namespace DTLib 8 { 9 10 /* 建立指定字符串的 pmt(部分匹配表)表 */ 11 int* String::make_pmt(const char* p) // O(m),只有一个 for 循环 12 { 13 int len = strlen(p); 14 int* ret = static_cast<int*>(malloc(sizeof(int) * len)); 15 16 if ( ret != NULL ) 17 { 18 int ll = 0; //定义 ll,前缀和后缀交集的最大长度数,largest length;第一步 19 ret[0] = 0; // 长度为 1 的字符串前后集都为空,对应 ll 为 0; 20 for(int i=1; i<len; i++) // 从第一个下标,也就是第二个字符开始计算,因为第 0 个字符前面已经计算过了; 第二步 21 { 22 /* 算法第四步 */ 23 while( (ll > 0) && (p[ll] != p[i]) ) // 当 ll 值为零时,转到下面 if() 函数继续判断,最后赋值与匹配表,所以顺序不要错; 24 { 25 ll = ret[ll - 1]; // 从之前匹配的部分匹配值表中,继续和最后扩展的那个字符匹配 26 } 27 28 /* 算法的第三步,这是成功的情况 */ 29 if( p[ll] == p[i] ) // 根据 ll 来确定扩展的种子个数为 ll,而数组 ll 处就处对应的扩展元素,然后和最新扩展的元素比较; 30 { 31 ll++; // 若相同(与假设符合)则加一 32 } 33 34 ret[i] = ll; // 部分匹配表里存储部分匹配值 ll 35 } 36 } 37 38 return ret; 39 } 40 41 /* 在字符串 s 中查找子串 p */ 42 int String::kmp(const char* s, const char* p) // O(m) + O(n) ==> O(m+n), 只有一个 for 循环 43 { 44 int ret = -1; 45 int sl = strlen(s); 46 int pl = strlen(p); 47 int* pmt = make_pmt(p); 48 49 if( (pmt != NULL) && (0 < pl) && (pl <= sl) ) // 判断查找条件 50 { 51 for(int i=0, j=0; i<sl; i++) // i 的值要小于目标窜长度才可以查找 52 { 53 while( (j > 0) && (s[i] != p[j]) ) // 比对不上的时候,持续比对, 54 { 55 j = pmt[j-1]; //移动后应该继续匹配的位置,j =j-(j-LL)= LL = PMT[j-1] 56 } 57 58 if( s[i] == p[j] ) // 比对字符成功 59 { 60 j++; // 加然后比对下一个字符 61 } 62 63 if( j == pl ) // 这个时候是查找到了,因为 j 增加到了 pl 的长度; 64 { 65 ret = i + 1 - pl; // 匹配成功后,i 的值停在最后一个匹配成功的字符上,这样就返回匹配成功的位置 66 break; 67 } 68 } 69 } 70 71 free(pmt); 72 73 return ret; 74 } 75 76 /* 通过参数 s,具体产生当前字符串对象当中的数据,供构造函数使用;实现的方法就是封装 */ 77 void String::init(const char* s) 78 { 79 m_str = strdup(s); // 当前字符串当中的数据通过 m_str 指针指向; 80 81 if( m_str ) // 复制失败会返回空指针; 82 { 83 m_length = strlen(m_str); // 获取长度; 84 } 85 else 86 { 87 THROW_EXCEPTION(NoEnoughMemoryException, "No memory to creat String object ..."); 88 } 89 } 90 91 /* 比对函数,为 startWith() 服务,前两个参数为字符数组的首地址,第三个参数是字符数组长度;长度范围内字符数组对应元素都相等,返回真; */ 92 bool String::equal(const char* l, const char* r, int len) const 93 { 94 bool ret = true; 95 for(int i=0; i<len && ret; i++) // 这里的 ret 看似没用,实则只要有元素不相等就停止循环、非常重要; 96 { 97 ret = ret && (l[i] == r[i]); // 如果有一个位置的字符不相等,则结束比较,返回 false; 98 } 99 100 return ret; 101 } 102 103 String::String() 104 { 105 init(""); 106 } 107 108 String::String(const char* s) 109 { 110 init(s ? s : ""); // 将空指针转换为空字符串,s 正确了返回 s,错误了返回 “”(这是空字符串) 111 } 112 113 String::String(const String& s) 114 { 115 init(s.m_str); 116 } 117 118 /* 字符作为初始值创建字符串对象 */ 119 String::String(char c) 120 { 121 char s[] = {c, '