目录
1、历史遗留问题
C语言不支持真正意义上的字符串
C语言用字符数组和—组函数实现字符串操作
C语言不支持自定义类型,因此无法获得字符串类型
从C到C++的进化过程引入了自定义类型
在C++中可以通过类完成字符串类型的定义
C++中的原生类型系统不包含字符串类型
2、字符串类的创建
DTLib中字符串类的设计
实现时的注意事项
-无缝实现 String 对象与 char* 字符串的互操作
-操作符重载函数需要考虑是否支持 const 版本
-通过C语言中的字符串函数实现 String 的成员函数
3、编程实验
字符串类的实现 DTString.h DTString.cpp
#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 <cstring>
#include <cstdlib>
#include "DTString.h"
#include "Exception.h"
using namespace std;
namespace DTLib
{
void String::init(const char *s)
{
m_str = strdup(s); //m_str指向堆空间拷贝字符串
if( m_str )
{
m_length = strlen(m_str);
}
else
{
THROW_EXCEPTION(NoEnoughMemoryException,"No memory to create String object ...");
}
}
String::String()
{
init("");
}
String::String(const char *s)
{
init(s ? s : ""); //NULL指针转换为空字符串
}
String::String(const String &s)
{
init(s.m_str);
}
String::String(char c)
{
char s[] = {c, '\0'};
init(s);
}
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 ? 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);
}
}
main.cpp
#include <iostream>
#include "DTString.h"
using namespace std;
using namespace DTLib;
void test_1()
{
cout << "test_1() begin :" << endl;
String s;
s = 'N';
cout << s.str() << endl;
cout << s.length() << endl;
cout << (s == "N") << endl;
cout << (s > "CCC") << endl;
s += "yist";
cout << s.str() << endl;
cout << s.length() << endl;
cout << (s == "Nyist") << endl;
cout << "test_1() end :" << endl;
}
void test_2()
{
cout << "test_2() begin :" << endl;
String a[] = {"D","C","B","A"};
String min = a[0];
for(int i = 0; i < 4; i++)
{
if(min > a[i])
min = a[i];
}
cout << min.str() << endl;
cout << "test_2() end :" << endl;
}
int main()
{
test_1();
test_2();
return 0;
}
4、字符串类中的常用成员函数实现
字符串类中的常用成员函数
重载数组访问操作符[]
- char& operator[](int i);
- char operator[](int i) const;
char& String::operator [] (int i)
{
if((0 <= i) && (i < m_length))
{
return m_str[i];
}
else
{
THROW_EXCEPTION(IndexOutOfBoundsExpception, "Parameter i is invalid ...");
}
}
char String::operator [] (int i) const
{
return const_cast<String&>(*this)[i];
}
判断是否以指定字符串开始或结束
- bool startWith(const char* s) const;
- bool startWith(const String& s) const;
- bool endOf(const char* s) const;
- 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 && ret; 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); //equal为const函数
}
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);
}
在指定位置处插入字符串
- String& insert(int i, const char* s);
- String& insert(int i, const String& s);
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(IndexOutOfBoundsExpception, "Parameter i is invalid ...");
}
return *this;//返回引用,支持链式操作
}
String& String::insert(int i, const String& s)
{
return insert(i, s.m_str);
}
去掉字符串两端的空白字符
- String& trim();
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;
}
5、KMP算法的应用
思考
如何在目标字符串中查找是否存在指定的子串?
字符串类中的新功能
子串查找(KMP算法的直接运用)
int* String::make_pmt(const char* p) //O(n)
{
int len = strlen(p);
int* ret = static_cast<int*>(malloc(sizeof(int)*len));
if(ret != NULL)
{
int ll = 0; //前后缀交集元素最大长度
ret[0] = 0; //长度为1的字符串ll值为0
for(int i=1;i<len;i++)
{
while((ll > 0) && (p[ll] != p[i]))
{
ll = ret[ll-1];
}
if(p[ll] == p[i])
{
ll++;
}
ret[i] = ll; //部分匹配表存放部分匹配值
}
}
return ret;
}
int String::kmp(const char* s,const char* p) //O(m)+O(n)=O(m+n)
{
int ret = -1;
int s1 = strlen(s);
int p1 = strlen(p);
int* pmt = make_pmt(p);
if((pmt != NULL) && (0 < p1) && (p1 <= s1))
{
for(int i=0,j=0;i<s1;i++)
{
while((j > 0) && (s[i] != p[j]) )
{
j = pmt[j-1];
}
if(s[i] == p[j])
{
j++;
}
if(j == p1)
{
ret = i + 1 - p1;
break;
}
}
}
free(pmt);
return ret;
}
int String::indexOf(const char* s) const
{
return kmp(m_str,s ? s : "");
}
int String::indexOf(const String& s) const
{
return kmp(m_str,s.m_str);
}
在字符串中将指定的子串删除
1. 根据KMP在目标字符串中查找子串的位置
2. 通过子串位置和子串长度进行删除
String& String::remove(int i,int len)
{
if((0 <= i)&&(i < m_length))
{
int n = i;
int m = i + len;
while((n < m) && (m < m_length))
{
m_str[n++] = m_str[m++];
}
m_str[n] = '\0';
m_length = n;
}
return *this;
}
String& String::remove(const char* s)
{
return remove(indexOf(s),s ? strlen(s) : 0);
}
String& String::remove(const String& s)
{
return remove(indexOf(s.m_str),s.length());
}
字符串的减法操作定义( operator - )
-使用remove实现字符串间的减法操作
字符串自身不被修改
返回产生的新串
String String::operator - (const String& s) const
{
return String(*this).remove(s);//调用构造函数产生临时对象
}
String String::operator - (const char* s) const
{
return String(*this).remove(s);
}
String& String::operator -= (const String& s)
{
return remove(s);
}
String& String::operator -= (const char* s)
{
return remove(s);
}
字符串中的子串替换
String& String::replace(const char* t,const char* s)
{
int index = indexOf(t);
if(index >= 0)
{
remove(t);
insert(index,s);
}
return *this;
}
String& String::replace(const String& t,const char* s)
{
return replace(t.m_str,s);
}
String& String::replace(const char* t,String& s)
{
return replace(t,s.m_str);
}
String& String::replace(const String& t,const String& s)
{
return replace(t.m_str,s.m_str);
}
从字符串中创建子串
- String sub(int i, int len) const
以i为起点提取长度为len的子串
子串提取不会改变字符串本身的状态
6、编程实验
常用成员函数的实现 DTString.h DTString.cpp
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);
bool equal(const char* l, const char* r, int len) const;
static int* make_pmt(const char* p) ;
static int kmp(const char* s,const char* p);
public:
String();
String(char c);
String(const char* s);
String(const String& s);
int length() const;
const char* str() const;
bool startWith(const char* s) const;
bool startWith(const String& s) const;
bool endOf(const char* s) const;
bool endOf(const String& s) const;
String& insert(int i, const char* s);
String& insert(int i, const String& s);
String& trim();
int indexOf(const char* s) const;
int indexOf(const String& s) const;
String& remove(int i, int len);
String& remove(const char* s);
String& remove(const String& s);
String& replace(const char* t, const char* s);
String& replace(const String& t, const char* s);
String& replace(const char* t, String& s);
String& replace(const String& t, const String& s);
String sub(int i, int len) const;
char& operator [] (int i);
char operator [] (int i) 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) 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 <cstring>
#include <cstdlib>
#include "DTString.h"
#include "Exception.h"
using namespace std;
namespace DTLib
{
int* String::make_pmt(const char* p) //O(n)
{
int len = strlen(p);
int* ret = static_cast<int*>(malloc(sizeof(int) * len));
if(ret != NULL)
{
int ll = 0; //前后缀交集元素最大长度
ret[0] = 0; //长度为1的字符串ll值为0
for(int i = 1; i < len; i++)
{
while((ll > 0) && (p[ll] != p[i]))
{
ll = ret[ll-1];
}
if(p[ll] == p[i])
{
ll++;
}
ret[i] = ll; //部分匹配表存放部分匹配值
}
}
return ret;
}
int String::kmp(const char* s, const char* p) //O(m)+O(n)=O(m+n)
{
int ret = -1;
int s1 = strlen(s);
int p1 = strlen(p);
int* pmt = make_pmt(p);
if((pmt != NULL) && (0 < p1) && (p1 <= s1))
{
for(int i = 0, j = 0; i < s1; i++)
{
while((j > 0) && (s[i] != p[j]) )
{
j = pmt[j-1];
}
if(s[i] == p[j])
{
j++;
}
if(j == p1)
{
ret = i + 1 - p1;
break;
}
}
}
free(pmt);
return ret;
}
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(const char *s)
{
init(s ? s : ""); //NULL指针转换为空字符串
}
String::String(const String &s)
{
init(s.m_str);
}
String::String(char c)
{
char s[] = {c, '\0'};
init(s);
}
int String::length() const
{
return m_length;
}
const char* String::str() const
{
return m_str;
}
bool String::equal(const char* l, const char* r, int len) const
{
bool ret = true;
for(int i = 0; i < len && ret; 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); //equal为const函数
}
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);
}
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(IndexOutOfBoundsExpception, "Parameter i is invalid ...");
}
return *this;
}
String& String::insert(int i, const String& s)
{
return insert(i, s.m_str);
}
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;
}
int String::indexOf(const char* s) const
{
return kmp(m_str, s ? s : "");
}
int String::indexOf(const String& s) const
{
return kmp(m_str, s.m_str);
}
String& String::remove(int i, int len)
{
if((0 <= i) && (i < m_length))
{
int n = i;
int m = i + len;
while((n < m) && (m < m_length))
{
m_str[n++] = m_str[m++];
}
m_str[n] = '\0';
m_length = n;
}
return *this;
}
String& String::remove(const char* s)
{
return remove(indexOf(s), s ? strlen(s) : 0);
}
String& String::remove(const String& s)
{
return remove(indexOf(s.m_str), s.length());
}
String& String::replace(const char* t, const char* s)
{
int index = indexOf(t);
if(index >= 0)
{
remove(t);
insert(index, s);
}
return *this;
}
String& String::replace(const String& t, const char* s)
{
return replace(t.m_str, s);
}
String& String::replace(const char* t, String& s)
{
return replace(t, s.m_str);
}
String& String::replace(const String& t, const String& s)
{
return replace(t.m_str, s.m_str);
}
String String::sub(int i, int len) const
{
String ret;
if((0 <= i)&&(i < m_length))
{
if(len < 0) len = 0;
if(len > m_length) len = m_length - i;
char* str = reinterpret_cast<char*>(malloc(len + 1));
if(str)
{
strncpy(str, m_str + i, len);
str[len] = '\0';
}
ret = str;
return ret;
}
else
{
THROW_EXCEPTION(IndexOutOfBoundsExpception, "Parameter i is invalid ...");
}
}
char& String::operator [] (int i)
{
if((0 <= i) && (i < m_length))
{
return m_str[i];
}
else
{
THROW_EXCEPTION(IndexOutOfBoundsExpception, "Parameter i is invalid ...");
}
}
char String::operator [] (int i) const
{
return const_cast<String&>(*this)[i];
}
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 ? 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) const
{
return String(*this).remove(s);//调用构造函数产生临时对象
}
String String::operator - (const char* s) const
{
return String(*this).remove(s);
}
String& String::operator -= (const String& s)
{
return remove(s);
}
String& String::operator -= (const char* s)
{
return remove(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);
}
}
main1.cpp
#include <iostream>
#include"DTString.h"
using namespace std;
using namespace DTLib;
int main()
{
/*
String s = "nyist.edu.cn";
for(int i = 0; i < s.length(); i++)
{
cout << s[i] << " ";
}
cout << endl;
cout << s.startWith("nyist") << endl;
cout << s.endOf(".cn") << endl;
s.insert(0, "http://www.");
cout << s.str() << endl;
*/
String s = " nyist.edu.cn ";
if(s.trim().insert(0, "http://www.").endOf(".cn") && s.startWith("http://"))
{
cout << s.str() << endl;
}
return 0;
}
main2.cpp
#include <iostream>
#include <cstring>
#include <cstdlib>
#include "DTString.h"
using namespace std;
using namespace DTLib;
int main()
{
String s = "ababax";
cout << s.indexOf("ba") << endl;
cout << s.remove(1, 4).str() << endl;
cout << s.remove("a").str() << endl;
s.insert(0, "abcdefg");
cout << s.str() << endl;
String s1 = s - "fgx";
cout << s1.str() << endl;
s -= "abc";
cout << s.str() << endl;
s -= s;
s.insert(0, "abcabc");
cout << s.str() << endl;
s.replace("abc", "cba");
cout << s.str() << endl;
s.insert(0, "abcabc");
cout << s.str() << endl;
String s2 = s.sub(3, 10);
cout << s2.str() << endl;
return 0;
}
感谢评论区朋友指出的错误,现已修正
7、小结
C / C++语言本身不支持字符串类型
C语言通过字符数组和一组函数支持字符串操作
C++通过自定义字符串类型支持字符串操作
字符串类型通过C语言中的字符串函数实现
字符串类是工程开发中必不可少的组件
字符串中应该包含常用字符串操作函数
-增: insert , operator + , …
-删: remove , operator - , …
-查: indexOf, …
-改: replace, …