在C语言中,没有类这个概念,处理字符串通常是把字符串存在字符数组中处理的,并且库中提供了大量的字符串处理函数,但是其实与C++相比显的并不是很好用,C++中封装了string这个类,底层也是字符数组,在这基础上类内封装了大量的处理字符串的方法,
下面来一一的介绍一下。
首先推荐一个C++文档的网站:cplusplus
可以看到string类其实是typedef出来的,最初的那个类叫做basic_string这个类,由于编码的形式有很多种,一个字符占用的空间不同有的1个字节的2个字节有的4个字节,所以有不同的string类。
最常用的还是basic_string< char >这个类。
可以看到构造函数的重载形式非常多,下面我只演示最常用的几个。
1,使用字符串初始化
int main()
{
string str = "hello string";
cout << str << endl;
return 0;
}
int main()
{
string str = "hello string";
cout << str << endl;
string str1(str);
cout << str1 << endl;
return 0;
}
int main()
{
string str1 = "hello string";
string str2(str1, 0, 8);
cout << str2 << endl;
return 0;
}
4,使用字符串的前几个字符构造
int main()
{
string str1("hello string", 5);
cout << str1 << endl;
return 0;
}
int main()
{
string str1;
string str2 = "hello C++";
str1 = str2;
cout << str1 << endl;
return 0;
}
2,字符串做参数
int main()
{
string str1;
str1 = "hello C++";
cout << str1 << endl;
return 0;
}
3,单个字符作参数
int main()
{
string str1;
str1 = 'C';
cout << str1 << endl;
return 0;
}
int main()
{
string str1 = "hello world";
cout << "size() : " << str1.size() << endl
<< "length() : " << str1.length() << endl;
return 0;
}
其实这两个函数功能是重叠的,实现是一摸一样的,字符串用length更合理一些,但是后面的容器都是使用的size,所以为了统一string类也提供了size这个接口。
capcity
capcity就是返回其容量
resize与reserve
resize 是重新调整string的大小,如果传入的参数大于当前的大小,那么就会扩容加初始化,如果小于当前的大小就会改变其有效长度(容量不变)。
int main()
{
string str1 = "hello world";
str1.resize(20);
str1.resize(5);
return 0;
}
reserve重新调整其容量,只会在传入参数大于当前容量时起作用,传入参数大于当前容量时会扩容不加初始化。
int main()
{
string str1 = "hello world";
cout << str1.capacity() << endl;
str1.reserve(5);
cout << str1.capacity() << endl;
str1.reserve(20);
cout << str1.capacity() << endl;
return 0;
}
最常用的就是[ ]访问string的任意元素,at()与其功能类似只是对越界的检查方式不同,operator[ ]采用的是断言方式检查,at采用的是温柔的检查方式。
int main()
{
string str1("hello world");
for (int i = 0; i < str1.size(); i++)
{
cout << str1[i] << " ";
}
cout << endl;
for (int i = 0; i < str1.size(); i++)
{
cout << str1.at(i) << " ";
}
cout << endl;
return 0;
}
1,append与operator+=
两个功能是一样的,只是append的接口更多一些,但是append并没有operator+=更常用一些。
2,尾插和尾删
3,任意位置插入删除
4,交换两个string对象
int main()
{
string str1("hello string");
const char* s = str1.c_str();
cout << s << endl;
return 0;
}
一个是从前往后查找,一个是从后往前查找,查找的内容可以是,字符,字符串,string类对象。返回的是查找到的字串的位置下标。
字串切割函数,将一个string类对象从pos位置开始长度为len的字串返回,这里的nops是无符号整型的最大值。如果长度大于原字符串本身那么就将从pos位置开始直到结尾的字串返回。
4,compare
这个函数其实也并不常用,主要因为string类重载了大量的关系运算符。
由于string类是一个自定义类型,无法使用cin,cout对其进行输入和输出,所以要对>>和<<操作符进行重载。
使用cin>>读取string的时候,遇到空格就会停止,所以我们想输入一个带有空格的字符串就会出现问题,而getline这个函数就可以避免这个问题,它默认是以读取到’\n’为结束标志的,也给以手动改变其结束标志。
1,大体框架
namespace gy
{
class string
{
public:
string(const char* str = "")
:_size(strlen(str))
{
_capcity = _size == 0 ? 4 : _size;
_str = new char[_capcity + 1];
strcpy(_str, str);
}
private:
char* _str;
size_t _size;
size_t _capcity;
static const size_t npos = -1;
};
}
2,拷贝构造函数
string(const string& s)
:_size(s._size)
, _capcity(s._capcity)
{
_str = new char[_capcity + 1];
strcmp(_str, s._str);
}
3,赋值运算符重载
string& operator=(const string& s)
{
_size = s._size;
_capcity = s._capcity;
char* tmp = new char[_capcity + 1];
strcpy(tmp, s._str);
delete[] _str;
_str = tmp;
return *this;
}
4,析构函数
~string()
{
delete[] _str;
_str = nullptr;
_size = _capcity = 0;
}
5,c_str函数
const char* c_str()
{
return _str;
}
6,size与capcity
size_t size() const
{
return _size;
}
size_t capcity() const
{
return _capcity;
}
由于这样的函数并不会对string的内容进行修改,最好加上const这样就能使普通对象和const对象都能调用。
7,operator[ ]
char& operator[](size_t n)
{
assert(n < _size);
return _str[n];
}
const char& operator[](size_t n) const
{
assert(n < _size);
return _str[n];
}
8,reserve
void reserve(size_t n)
{
if (n > _capcity)
{
char* tmp = new char[n + 1];
strcpy(tmp, _str);
delete[] _str;
_str = tmp;
_capcity = n;
}
}
9,insert
insert 重载两个版本,一个是插入字符,一个是插入字符串
string& insert(size_t pos, char ch)
{
assert(pos <= _size);
//插入前检查是否需要扩容
if (_size + 1 > _capcity)
reserve(_size * 2);
//挪动数据
size_t end = _size;
while (end >= pos)
{
_str[end + 1] = _str[end];
--end;
}
_str[pos] = ch;
_size++;
return *this;
}
如果采用这种写法,你在测试的是时候会发现程序一直在跑停不下来。
这是由于我们采用的下标都是size_t的类型的,如果pos为0,最后一次挪动数据的时候,end处于0位置,挪动完后–end,end会减成 -1,而-1对于无符号整数来说就是最大值,所以会造成死循环。所以我们得换种写法。
string& insert(size_t pos, char ch)
{
assert(pos <= _size);
//插入前检查是否需要扩容
if (_size + 1 > _capcity)
reserve(_size * 2);
//挪动数据
size_t end = _size + 1;
while (end > pos)
{
_str[end] = _str[end - 1];
--end;
}
_str[pos] = ch;
_size++;
return *this;
}
插入字符串
插入字符串的时候同样需要注意上面的死循环问题。
string& insert(size_t pos, const char* s)
{
assert(pos <= _size);
size_t lens = strlen(s);
if (_size + lens > _capcity)
reserve(_size + lens);
//挪动数据
size_t end = _size + lens;
while (end > pos + lens - 1)
{
_str[end] = _str[end - lens];
--end;
}
strncpy(_str, s, lens);
_size += lens;
return *this;
}
10,erase
string& erase(size_t pos, size_t len = npos)
{
assert(pos < _size);
//挪动数据
if (len == npos || _size - pos <= len)
{
_str[pos] = '\0';
_size = pos;
}
else
{
size_t begin = pos + len;
while (begin <= _size)
{
_str[begin - len] = _str[begin];
++begin;
}
_size -= len;
}
return *this;
}
11,push_back, pop_back, append
这些接口都可以使用上面的insert和erase复用。
void push_back(char ch)
{
insert(_size, ch);
}
void pop_back()
{
erase(_size - 1);
}
void append(const char* s)
{
insert(_size, s);
}
12,operator+=
+=一个字符
void operator+=(char ch)
{
push_back(ch);
}
+=字符串
void operator+=(const char* s)
{
append(s);
}
这些函数都在复用insert和erase,可见insert和erase一旦实现,就已经完成了许多工作。
13,swap
成员函数的swap非常简单,只需要交换内部为何的_str,_size,_capcity即可。
void swap(string& s)
{
std::swap(_str, s._str);
std::swap(_size, s._size);
std::swap(_capcity, s._capcity);
}
14,find
find 一个字符
size_t find(char ch,size_t pos = 0)
{
assert(pos < _size);
for (int i = pos; i < size(); i++)
{
if (_str[i] == ch)
return i;
}
return npos;
}
find 一个字符串
size_t find(const char* s, size_t pos = 0)
{
assert(pos < _size);
char* res = strstr(_str + pos, s);
if (!res)
return npos;
return res - _str;
}
15,substr
string substr(size_t pos, size_t len = npos)
{
assert(pos < _size);
string res;
size_t curlen = len;
if (len <= _size - pos || len == npos)
curlen = _size - pos;
char* tmp = new char[curlen + 1];
strncpy(tmp, _str + pos, curlen);
tmp[curlen] = '\0';
res += tmp;
delete[] tmp;
return res;
}
16,重载关系运算符
bool operator==(const string& str) const
{
return strcmp(_str, str._str) == 0;
}
bool operator>(const string& str) const
{
return strcmp(_str, str._str) > 0;
}
bool operator>=(const string& str) const
{
return (*this > str) || (*this == str);
}
bool operator<(const string& str) const
{
return !(*this >= str);
}
bool operator<=(const string& str) const
{
return !(*this > str);
}
bool operator!=(const string& str) const
{
return !(*this == str);
}
17,重载流插入操作符
friend ostream& operator<<(ostream& _cout, const string& str);//类内
ostream& operator<<(ostream& _cout, const string& str)
{
for (int i = 0; i < str.size(); i++)
{
if (str[i] == '\0')
cout << ' ';
else
cout << str[i];
}
return _cout;
}
18,重载流插入操作符
friend istream& operator>>(istream& _cin, string& str);//类内
istream& operator>>(istream& _cin, string& str)
{
str.clear();
char ch = cin.get();
//缓冲区提高效率
char tmp[128];
int i = 0;
while (ch != ' ' && ch != '\n')
{
tmp[i++] = ch;
if (i == 127)
{
tmp[127] = '\0';
str += tmp;
i = 0;
}
ch = cin.get();
}
if (i != 0)
{
tmp[i] = '\0';
str += tmp;
}
return _cin;
}
18,正向迭代器
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
const_iterator begin() const
{
return _str;
}
const_iterator end() const
{
return _str + _size;
}