前言
STL(standard template libaray-标准模板库):是C++标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架。
STL的六大组件
为什么学习string类?
C语言中,字符串是以’\0’结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。
在OJ中,有关字符串的题目基本以string类的形式出现,而且在常规工作中,为了简单、方便、快捷,基本都使用string类,很少有人去使用C库中的字符串操作函数。
目录
1.string类
2.string类的常用接口说明
3.模拟实现string
1.string类
- 字符串是表示字符序列的类
- 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作
单字节字符字符串的设计特性。 - string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型(关于模板的更多信
息,请参阅basic_string)。 - string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_traits
和allocator作为basic_string的默认参数(根于更多的模板信息请参考basic_string)。 - 注意,这个类独立于所使用的编码来处理字节:如果用来处理多字节或变长字符(如UTF-8)的序列,这个
类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。
总结: - string是表示字符串的字符串类
- 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。
- string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator>string;
- 不能操作多字节或者变长字符的序列。
在使用string类时,必须包含#include头文件以及using namespace std;
2.string 类的常用接口说明
2.1string类对象的常见构造
constructor | 功能说明 |
---|---|
string() | 构造空的string 对象,即空字符串 |
string (const char * str) | 用C-string来构造string类对象 |
string(size_t n ,char c) | string 类对象中包含n个字符c |
string (const string& s) | 拷贝构造函数 |
代码测试
#include <iostream>
#include<string>
using namespace std;//string类的常用接口测试void test_string()
{string s1("");//空字符串cout << s1 << endl;string s2("hello world");//用C-string来构造string类对象cout << s2 << endl;string s3(8, '*'); //string 类对象中包含n个字符ccout << s3 << endl;string s4(s3);//拷贝构造函数cout << s4 << endl;
}int main()
{test_string();return 0;
}
2.2 string 类的容量操作
函数名称 | 功能说明 |
---|---|
size | 返回字符串有效字符长度 |
length | 返回字符串有效字符长度 |
capacity | 返回空间总大小 |
empty | 检测字符串释放为空串,是返回true,否则返回false |
clear | 清空有效字符 |
reserve | 为字符串预留空间 |
resize | 将有效字符的个数该成n个,多出的空间用字符c填充 |
- size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一致,一般情况下基本都是用size()。
代码测试:
void test_string3()
{string s1;s1.resize(15, '*');cout << s1.size() << endl;cout << s1.length() << endl;cout << s1.capacity() << endl;cout << s1 << endl;s1.resize(20, '#');cout << s1.size() << endl;cout << s1.length() << endl;cout << s1.capacity() << endl;cout << s1 << endl;string s2;s2.resize(15, '&');cout << s2.size() << endl;cout << s2.length() << endl;cout << s2.capacity() << endl;cout << s2 << endl;s2[0]++;s2.at(0)++;cout << s2 << endl;
}
运行结果:
- clear()只是将string中有效字符清空,不改变底层空间大小。
代码测试:
void test_stringClear()
{string s("hello world");cout << s << endl;s.clear();cout << s << endl;cout << s.size() << endl;cout << s.capacity() << endl;}
运行结果:
- resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。
代码测试:
void test_stringResize()
{string s1;s1.resize(15, '*');cout << s1.size() << endl;cout << s1.length() << endl;cout << s1.capacity() << endl;cout << s1 << endl;s1.resize(20, '#');cout << s1.size() << endl;cout << s1.length() << endl;cout << s1.capacity() << endl;cout << s1 << endl;string s2;s2.resize(15, '&');cout << s2.size() << endl;cout << s2.length() << endl;cout << s2.capacity() << endl;cout << s2 << endl;s2[0]++;s2.at(0)++;cout << s2 << endl;
}
运行结果:
- reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于string的底层空间总大小时,reserver不会改变容量大小。
代码测试:
void test_stringReserve()
{string s;s.reserve(100);size_t old = s.capacity();cout << "初始:" << s.capacity() << endl;for (size_t i = 0; i < 100; i++){s.push_back('m');if (s.capacity() != old){cout << "扩容:" << s.capacity() << endl;old = s.capacity();}}s.reserve(10);cout << s.capacity() << endl;
}
运行结果:
2.3 string类对象的访问及遍历操作
函数名称 | 功能说明 |
---|---|
operator[ ] | 返回pos位置的字符,const string类对象调用 |
begin() , end() | begin获取第一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器 |
rbegin(),rend() | begin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器 |
范围for | C++11支持更简洁的范围for的新遍历方式 |
代码测试
void test_stringFor()
{string s1("hello world");string s2 = "hello world";for (size_t i = 0; i < s1.size(); i++){cout << s1[i];}cout << endl;string::reverse_iterator rit = s1.rbegin();while (rit != s1.rend()){cout << *rit << " ";rit++;}cout << endl;for (auto ch : s1){cout << ch << " ";}cout << endl;
}
运行结果:
2.4 string类对象的修改操作
函数名称 | 功能说明 |
---|---|
push_back | 在字符串后尾插字符c |
append | 在字符串后追加一个字符串 |
operator+= | 在字符串后追加字符串str |
c_str | 返回C格式字符串 |
find + npos | 从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置 |
rfind | 从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置 |
substr | 在str中从pos位置开始,截取n个字符,然后将其返回 |
代码测试
void test_string5()
{string s1("test.cpp.tar.zip");//size_t i = s1.find('.');size_t i = s1.rfind('.');string s2 = s1.substr(i);cout << s2 << endl;//string s3("https://legacy.cplusplus.com/reference/string/string/rfind/");string s3("https://cn.bing.com/search?pglt=41&q=baidu&cvid=b6332837d8f642d1befbdeeaae18733b&aqs=edge..69i57j0l8.1965j0j1&FORM=ANSPA1&PC=EDGEDBB");string s4;string s5;string s6;size_t j = s3.find(':');if (j != string::npos)s4 = s3.substr(0, j);elsecout << "没有找到" << endl;size_t m = s3.find('/', j + 3);if (m != string::npos)s5 = s3.substr(j + 3, m - (j + 3));elsecout << "没有找到" << endl;s6 = s3.substr(m);cout << s4 << endl;cout << s5 << endl;cout << s6 << endl;
}
运行结果:
3.模拟实现string
string.h
#pragma once
#include <assert.h>
namespace xiaobai
{class string{public:typedef char* iterator;typedef const char* const_iterator;iterator begin(){return _str;}iterator end(){return _str + _size;}const_iterator begin() const{return _str;}const_iterator end() const{return _str + _size;}char& operator[](size_t n){assert(n < _size);return _str[n];}const char& operator[](size_t n) const{assert(n < _size);return _str[n];}string(const char* str = ""):_size(strlen(str)), _capacity(_size){_str = new char[_capacity + 1];strcpy(_str, str);}~string(){delete[] _str;_str = nullptr;_size = _capacity = 0;}size_t capacity() const{return _capacity;}size_t size() const{return _size;}const char* c_str() const{return _str;}void reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}void push_back(char ch){if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}_str[_size] = ch;_size++;_str[_size] = '\0';}void append(const char* str){size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}strcpy(_str + _size, str);_size += len;}string& operator+=(char ch){push_back(ch);return *this;}string& operator+=(const char* str){append(str);return *this;}// 在pos位置上插入字符c/字符串str,并返回该字符的位置string& insert(size_t pos, char c){assert(pos <= _size);if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}size_t end = _size + 1;while (end > pos){_str[end] = _str[end - 1];end--;}_str[pos] = c;_size++;return *this;}string& insert(size_t pos, const char* str){assert(pos <= _size);size_t len = strlen(str);size_t end = _size +len +1;if (end> _capacity){reserve(end);}while (end > pos){_str[end] = _str[end - strlen(str)-1];end--;}// 拷贝插入strncpy(_str + pos, str, len);_size += len;return *this;}string& erase(size_t pos, size_t len = npos){if (len == npos || pos + len >= _size) //pos开始到结尾都要删除{_str[pos] = '\0';_size = pos;}else{memmove(_str + pos, _str + pos + len, len);_size -= len;}return *this;}bool operator<(const string& s) const{return strcmp(_str, s._str) < 0;}bool operator<=(const string& s) const{return (*this < s)||(*this==s);}bool operator>(const string& s) const{return !(*this <= s);}bool operator>=(const string& s) const{return !(*this < s);}bool operator==(const string& s) const{return strcmp(_str, s._str) == 0;}bool operator!=(const string& s) const{return (!(*this == s));}void clear(){_str[0] = '\0';_size = 0;}private:char* _str;size_t _size;size_t _capacity;const static size_t npos;};const size_t string::npos = -1;ostream& operator<<(ostream& out, const string& str){for (auto ch : str){out << ch;}return out;}istream& operator>>(istream& in,string& str){str.clear();char ch;ch = in.get();str.reserve(128);while (ch != ' ' && ch != '\n'){if (str.size() == str.capacity()){str.reserve(str.capacity() * 2);}str += ch;}return in;}void test_string1(){string str1("hello world");cout << str1.c_str()<< endl;for (size_t i = 0; i < str1.size(); i++){cout << str1[i] << " ";}cout << endl;string::iterator it = str1.begin();while (it != str1.end()){cout << *it << " ";it++;}cout << endl;for (auto ch : str1){cout << ch << " ";}cout << endl;}void test_string2(){string s1("hello world");cout << s1.c_str() << endl;s1.push_back(' ');s1.append("hello bit");s1 += '#';s1 += "hello linux";cout << s1.c_str() << endl;}void test_string3(){string s1("hello bit!");cout<<s1.insert(5, '#')<<endl;cout << s1 << endl;cout << s1.insert(3, "hello xiaobai") << endl;cout << s1 << endl;cout<<s1.erase(2)<<endl;cout << s1 << endl;cout << (s1 > "hello world") << endl;cout << (s1 < "hello world") << endl;cout << (s1 <= "hello world") << endl;cout << (s1 >= "hello world") << endl;cout << (s1 == "hello world") << endl;cout << (s1 != "hello world") << endl;cout << s1.insert(1,'#')<<endl;cout << s1 << endl;}
}
test.cpp
#include<iostream>
#include<string>
#include<vector>
#include<list>using namespace std;
#include"string.h"int main()
{//xiaobai::test_string1();//xiaobai::test_string2();xiaobai::test_string3();return 0;
}