一、标准库的string类
1.1 string类
string类的文档介绍:cplusplus.com/reference/string/string/?kw=string
使用string类之前需要包含#include头文件以及using namespace std。
string类的底层定义:
cpp
typedef basic_string<char> string;
注:basic_string是一个模板类,basic_string<char>是使用模板类显示实例化专门用于存储字符的类,string是该类的重命名。string是一个字符串类。
string类里面npos的介绍:
cpp
static const size_t npos = -1;
注:npos是一个公共的静态成员变量,类型为size_t(unsigned int),大小为-1(4294967295)。
string设计成模板的原因 :
string类有4种分别是string、u16string、u32string以及wstring。在字符串里可能会出现汉字或其他更复杂的文字等,为了解决不同国家之间的编码需求,国际上提出了UTF-8、UTF-16、UTF-32三种编码格式。string是适配UTF-8编码格式的类,用于管理char*,u16string是适配UTF-16编码格式的类,用于管理char16_t*,u32string是适配UTF-32编码格式的类,用于管理char32_t*,wstring是用于管理wchar_t*的类,但能够被u16string和u32string取代。因此string便被设计成了模板,可以支持这几种不同的char类型。
1.2 string类的接口介绍
1.2.1 string类的构造函数(constructor)
默认构造函数(常用):
cpp
string();
使用:
cpp
string s1;
string s2;
使用常量字符串构造string对象(常用):
cpp
string(const char* s);
使用:
cpp
string s1("hello world"); //"hello world"
string s2("ABCDEF"); //"ABCDEF"
拷贝构造函数(常用):
cpp
string(const string& str);
使用:
cpp
string s1("hello world"); //"hello world"
string s2(s1); //"hello world"
使用str的一部分构造新对象,从pos位置开始len个字符:
cpp
string(const string& str, size_t pos, size_t len = npos);
参数介绍:
str:已经存在的对象,可以是匿名对象,也可以是使用字符串隐式类型转换构造的对象。
pos:起始字符位置,pos是一个无符号整型的下标。
len:要构造的字符串长度,且带有一个npos缺省参数。
npos:无符号整型,大小为4294967295。
cpp
static const size_t npos = -1;
注:如果len太大,那么构造方式会成为从pos开始到字符串结束的位置。
使用:
cpp
string s1("hello world"); //"hello world"
string s2(s1, 6, 5); //"world"
//匿名对象
string s3(string("hello world"), 6, 5); //"world"
//使用字符串进行隐式类型转换构造的对象
string s4("hello world", 6, 5); //"world"
//len太大或使用len的缺省参数
string s5("hello worldxxxxxxxxxxxxxxxxxx"); //"hello worldxxxxxxxxxxxxxxxxxx"
string s6(s5, 6); //"worldxxxxxxxxxxxxxxxxxx"
string s7(s5, 6, 1000); //"worldxxxxxxxxxxxxxxxxxx"
使用字符串s的前n个字符构造对象:
cpp
string(const char* s, size_t n);
使用:
cpp
string s1("hello world", 8); //"hello wo"
string s2("ABCDEFGH", 3); //"ABC"
使用n个字符构造对象:
cpp
string(size_t n, char c);
使用:
cpp
string s1(5, 'a'); //"aaaaa"
string s2(4, 'x'); //"xxxx"
使用一段迭代器区间去构造string对象:
cpp
template<typename InputIterator>
string(InputIterator first, InputIterator last);
使用:
cpp
string s1("hello world"); //"hello world"
string s2(s1.begin(), s1.end()); //"hello world"
string s3((s1.begin() + 1), (s1.end() - 2)); //"ello wor"
1.2.2 string类的析构函数(destructor)
默认析构函数:编译器会自动调用
cpp
~string();
1.2.3 string类的赋值重载函数(operator=)
默认赋值重载:
cpp
string& operator=(const string& str);
使用:
cpp
string s1("hello world"); //s1:"hello world"
string s2("xxxxx"); //s2:"xxxxx"
s1 = s2; //s1:"xxxxx",s2:"xxxxx"
使用常量字符串进行赋值重载:
cpp
string& operator=(const char* s);
使用:
cpp
string s1("aaa"); //s1:"aaa"
s1 = "bbb"; //s1:"bbb"
使用字符进行赋值重载:
cpp
string& operator=(char c);
使用:
cpp
string s1("aaa"); //s1:"aaa"
s1 = 'c'; //s1:"c"
注:s1使用字符重载后的成员变量仍然是一个字符串,只不过该字符串只有一个字符。
1.2.4 string类的迭代器(Iterator)
正向迭代器begin和end:
begin和end返回的迭代器需要用iterator进行接收。
begin:返回指向字符串第一个字符位置的迭代器。
cpp
iterator begin(); //非const版本
const_iterator begin() const; //const版本
end:返回指向字符串最后一个有效字符下一个位置的迭代器。
cpp
iterator end(); //非const版本
const_iterator end() const; //const版本

非const版本的正向迭代器:可以进行修改和访问
cpp
//非const版本,可以进行访问数据和修改数据
string s1("hello world");
string::iterator bit = s1.begin();
string::iterator eit = s1.end();
//修改
*bit = 'a'; //s1:"aello world"
*--eit = 'h'; //s1:"aello worlh"
const版本的正向迭代器:只能进行访问数据,无法对数据进行修改
cpp
//const版本,只能进行访问数据,无法进行修改数据
const string s2("hello world");
string::const_iterator cbit = s2.begin();
string::const_iterator ceit = s2.end();
//可以访问
cout << *cbit << endl;
cout << *ceit << endl;
//不能修改
//error C3892: "cbit": 不能给常量赋值
*cbit = 'a';
//error C3892: "ceit": 不能给常量赋值
*--ceit = 'd';
注:迭代器iterator是每个容器通用的名称,因此在使用时需要用类型进行指定。且const对象只能使用const版本的迭代器。
反向迭代器rbegin和rend:
rbegin和rend返回的迭代器需要用reverse_iterator进行接收。
rbegin:指向字符串的最后一个有效字符的位置.
cpp
reverse_iterator rbegin(); //非const版本
const_reverse_iterator rbegin() const; //const版本
rend:指向字符串第一个字符的前一个位置。
cpp
reverse_iterator rend(); //非const版本
const_reverse_iterator rend() const; //const版本

注:对反向迭代器的++是反方向移动,例如++rbit就是从后向前走。
非const版本的反向迭代器:可以进行修改和访问
cpp
//非const版本
string s1("hello world");
string::reverse_iterator rbit = s1.rbegin();
string::reverse_iterator reit = s1.rend();
//访问和修改
*(++rbit) = 'c'; //s1:"hello worcd"
*(--reit) = 'f'; //s1:"fello worcd"
const版本的反向迭代器:只能进行访问,无法进行修改
cpp
//const版本
const string s2("hello world");
string::const_reverse_iterator crbit = s2.rbegin();
string::const_reverse_iterator creit = s2.rend();
//访问
cout << *crbit << endl;
cout << *creit << endl;
//修改
//error C3892: "crbit": 不能给常量赋值
*crbit = 'e';
//error C3892: "creit": 不能给常量赋值
*creit = 'l';
注:如果直接访问rend返回的迭代器可能会报错,因为rend返回的迭代器位置不在字符串正常范围内。
1.2.5 string类容量相关的函数(Capacity)
不同编译器的扩容规则,在VS编译器下string扩容会以1.5倍的方式进行扩容,在Linux编译器下string会以2倍的方式进行扩容。左边的是Linux,右边的是VS。

(常用)size :返回对象内字符串的有效字符个数,不包含'\0'。
cpp
size_t size() const;
使用:
cpp
string s1("hello world");
cout << s1.size() << endl; //s1.size():11
length :返回对象内字符串的有效字符个数,不包含'\0'。
cpp
size_t length() const;
使用:
cpp
string s1("hello world");
cout << s1.length() << endl; //s1.length():11
注:length是早期设计专门用于string的函数,但自从STL出现之后,为了和其他容器保持统一,string便推出了size这个函数,size更具有通用性。
max_size:返回能构造字符串的最大长度,是一个固定的值,且不同编译器之间也会有差别。
cpp
size_t max_size() const;
使用:
cpp
cout << string().max_size() << endl; //2147483647
注:string()是一个使用默认构造的匿名对象。
(常用)resize:将有效元素个数改成n个。
cpp
void resize(size_t n);
void resize(size_t n, char c);
使用:
cpp
string s1("hello world");
string s2("hello world");
s1.resize(5);
s2.resize(15);
cout << s1 << endl; //"hello"
cout << s2 << endl; //"hello world"

cpp
string s1("hello world");
string s2("hello world");
s1.resize(5, 'c');
s2.resize(15, 'c');
cout << s1 << endl; //"hello"
cout << s2 << endl; //"hello worldcccc"

【注意】resize(size_t n)和resize(size_t n,char c)都是将有效字符个数改为n个,不同的是当字符个数增多时,resize(size_t n)会用\0来填充多余的空间 ,resize(szie_t n,char c)会使用字符c来填充多余的空间。
使用resize时对底层容量和有效元素个数的影响:
cpp
string s1("hello world");
//初始容量
cout << s1.capacity() << endl; //15
cout << "size:" << s1.size() << endl; //11
//元素个数减少
s1.resize(5);
cout << s1.capacity() << endl; //15
cout << "size:" << s1.size() << endl; //5
//元素个数增加
s1.resize(10);
cout << s1.capacity() << endl; //15
cout << "size:" << s1.size() << endl; //10
//元素个数增加
s1.resize(20);
cout << s1.capacity() << endl; //31
cout << "size:" << s1.size() << endl; //20
注:resize在改变元素个数时,如果是将元素个数增加,那么可能会改变底层容量的大小 ,如果是将元素个数减少,那么底层空间的容量不会发生变化。
capacity:获取存储空间(容量)的大小。
cpp
size_t capacity() const;
使用:
cpp
string s1("hello world");
cout << s1.capacity() << endl; //15
string s2("xxx");
cout << s2.capacity() << endl; //15
(常用)reserve:为字符串进行预留空间。
cpp
void reserve(size_t n = 0);
使用:
cpp
string s1("hello world");
s1.reserve(5);
cout << s1.capacity() << endl; //15
cout << s1 << endl; //"hello world"
s1.reserve(10);
cout << s1.capacity() << endl; //15
cout << s1 << endl; //"hello world"
s1.reserve(20);
cout << s1.capacity() << endl; //31
cout << s1 << endl; //"hello world"
s1.reserve();
cout << s1.capacity() << endl; //31
cout << s1 << endl; //"hello world"
【注意】reserve在为string预留空间时不会改变字符串的有效元素个数 ,当reserve的参数小于string当前的容量时,不会对当前容量进行改变 ,当reserve的参数大于string当前的容量时,会进行扩容。
reverse的价值:在确定大概需要多少空间的情况下,reverse提前开好可以避免频繁扩容的消耗。
clear :清空string内字符串中有效元素,但不会去释放空间。
cpp
void clear();
使用:
cpp
string s1("hello world");
cout << s1 << endl; //"hello world"
cout << s1.size() << endl; //11
cout << s1.capacity() << endl; //15
s1.clear();
cout << s1 << endl; //""
cout << s1.size() << endl; //0
cout << s1.capacity() << endl; //15
empty:检测字符串是否为空串,如果是空串则返回true,不是空串则返回false。
cpp
bool empty() const;
使用:
cpp
string s1("hello world");
string s2;
cout << s1.empty() << endl; //0
cout << s2.empty() << endl; //1
注:clear()和resize(0)都可以使字符串清空。
1.2.6 string类的成员访问相关函数(Element access)
(常用)operator[]:返回字符串中指定位置的字符。pos是要进行访问的下标。
cpp
char& operator[](size_t pos); //非const版本
const char& operator[](size_t pos) const; //const版本
使用:对象[pos]
cpp
string s("hello world");
cout << s[7] << endl; //'o'
//越界访问会直接报错
cout << s[15] << endl;
【注意】operator[]内部是进行断言判断的,越界访问时会直接报错。
at:访问字符串中指定位置的字符。pos是访问下标。
cpp
char& at(size_t pos); //非const版本
const char& at(size_t pos) const; //const版本
使用:对象.at(pos)
cpp
string s("hello world");
cout << s.at(7) << endl; //'o'
//越界访问时会抛异常
cout << s.at(15) << endl;
【注意】at和operator[]不同的是at进行越界访问时会抛异常。
back(C++11):返回字符串的最后一个字符。
cpp
char& back(); //非const版本
const char& back() const; //const版本
使用:对象.back()
cpp
string s("hello world");
cout << s.back() << endl; //'o'
//如果是空字符串
string s1;
//调用时会报错
cout << s1.back() << endl;
【注意】空字符串无法调用back函数,否则会报错。
front:返回字符串的首个字符。
cpp
char& front(); //非const版本
const char& front() const; //const版本
使用:对象.front()
cpp
string s("hello world");
cout << s.front() << endl; //'h'
//如果是空字符串
string s1;
//调用时会报错
cout << s1.front() << endl;
【注意】空字符串无法调用front,否则会报错。
1.2.7 string类的修改函数(Modifiers)
(常用)operator+=:在对象字符串后面追加字符、字符串或另外一个对象。
cpp
string& operator+=(const string& str); //在对象字符串后面追加一个新对象的字符串
string& operator+=(const char* s); //在对象字符串后面追加一个常量字符串
string& operator+=(char c); //在对戏那个字符串后面追加一个字符
使用:
cpp
string s1;
string s2("hello");
//对象后面追加新对象的内容
s1 += s2;
cout << s1 << endl; //"hello"
//在对象后面追加一个字符
s1 += ' ';
cout << s1 << endl; //"hello "
//在对象后面追加常量字符串
s1 += "world";
cout << s1 << endl; //"hello world"
append:string类的追加函数。
cpp
//追加一个字符串对象
string& append(const string& str);
//追加一个字符串对象的一部分,从subpos位置开始,sublen长的字符
string& append(const string & str, size_t subpos, size_t sublen);
//追加一段常量字符串
string& append(const char* s);
//追加常量字符串的前n个字符
string& append(const char* s, size_t n);
//追加n个c字符
string& append(size_t n, char c);
//追加一段迭代器区间的内容
template<typename InputIterator>
string& append(InputIterator first, InputIterator last);
使用:
cpp
string s;
string s1("hello");
//追加一个字符串对象
s.append(s1);
cout << s << endl; //"hello"
//追加一个字符串对象的一部分,从sub位置开始,sublen长的字符
s.append(s1, 2, 2);
cout << s << endl; //"helloll"
//追加一段常量字符串
s.append("ABCD");
cout << s << endl; //"hellollABCD"
//追加常量字符串的前n个字符
s.append("ABCD", 2);
cout << s << endl; //"hellollABCDAB"
//追加n个c字符
s.append(4, 'x');
cout << s << endl; //"hellollABCDABxxxx"
//追加一段迭代器区间的内容
string s3(" world");
s.append(s3.begin(), s3.end());
cout << s << endl; //"hellollABCDABxxxx world"
push_back:尾插一个字符。
cpp
void push_back(char c);
使用:
cpp
string s;
s.push_back('A');
s.push_back('B');
s.push_back('A');
s.push_back('B');
cout << s << endl; //"ABAB"
assign :将一段字符或一个字符覆盖现有对象的内容。
cpp
string& assign(const string& str);
string& assign(const string& str, size_t subpos, size_t sublen);
string& assign(const char* s);
string& assign(const char* s, size_t n);
string& assign(size_t n, char c);
template<typename InputIterator>
string& assign(InputIterator first, InputIterator last);
使用:
cpp
string s1("hello world");
string s2("ABCD");
string s3("abcdefghijklmn");
string s4;
s1.assign(s2);
cout << s1 << endl; //"ABCD"
s1.assign(s3);
cout << s1 << endl; //"abcdefghijklmn"
s1.assign(s4);
cout << s1 << endl; //""
s1.assign(s2, 1, 1);
cout << s1 << endl; //"B"
s1.assign("hello world");
cout << s1 << endl; //"hello world"
s1.assign("hello world", 4);
cout << s1 << endl; //"hell"
s1.assign(4, 'x');
cout << s1 << endl; //"xxxx"
s1.assign(++s2.begin(), --s2.end());
cout << s1 << endl; //"BC"
【注意】使用assign时会覆盖现有字符串的内容。
insert:在pos位置插入一个字符串对象、字符串、n个字符、一段迭代器区间。是主要的字符串头插的方式。
cpp
string& insert(size_t pos, const string & str);
string& insert(size_t pos, const string& str, size_t subpos, size_t sublen);
string& insert(size_t pos, const char* s);
string& insert(size_t pos, const char* s, size_t n);
string& insert(size_t pos, size_t n, char c);
void insert(iterator p, size_t n, char c);
//插入一个字符
iterator insert(iterator p, char c);
template<typename InputIterator>
void insert(iterator p, InputIterator first, InputIterator last);
使用:
cpp
string s;
string s1("hello world");
s.insert(0, s1);
cout << s << endl; //"hello world"
s.insert(5, s1, 0, 5);
cout << s << endl; //"hellohello world"
//清空字符串内容
s.clear();
cout << s << endl; //""
s.insert(0, "ABCD");
cout << s << endl; //"ABCD"
s.insert(2, "ABCD", 3);
cout << s << endl; //"ABABCCD"
s.insert(5, 3, 'x');
cout << s << endl; //"ABABCxxxCD"
//在一个迭代器p位置插入n个字符c
s.insert(s.begin() + 2, 2, ' ');
cout << s << endl; //"AB ABCxxxCD"
//在一个迭代器p位置插入一个字符c
s.insert(s.begin(),'K');
cout << s << endl; //"KAB ABCxxxCD"
//在迭代器p位置插入一段迭代器区间的内容
s.insert(s.end(), s1.begin(), s1.begin() + 5);
cout << s << endl; //"KAB ABCxxxCDhello"
【注意】insert在插入一个字符时只能使用迭代器,不能使用下标。
erase:删除一段字符。
cpp
//删除从pos位置开始len长度的字符内容,如果len使用缺省参数时会删除到字符串末尾
string& erase(size_t pos = 0, size_t len = npos);
//删除迭代器p指向的字符
iterator erase(iterator p);
//删除迭代器区间(first,last)之间的字符内容
iterator erase(iterator first, iterator last);
使用:
cpp
string s1("hello world");
s1.erase(5, 1);
cout << s1 << endl; //"helloworld"
s1.erase(5);
cout << s1 << endl; //"hello"
s1.erase(s1.begin() + 2);
cout << s1 << endl; //"helo"
s1.erase(s1.begin() + 1, s1.end() - 1);
cout << s1 << endl; //"ho"
【注意】erase删除的迭代器区间是左闭右开区间**[first,last)** 。当len不传参使用默认缺省参数或者len大于string对象pos后面字符个数时,会全部删除掉后面的字符**。**
cpp
Iterators specifying a range within the string] to be removed:
[first,last). i.e., the range includes all the characters between
first and last, including the character pointed by first but not
the one pointed by last.
replace:替换一段区间的字符。但会涉及到大量的挪动数据。
cpp
string& replace(size_t pos, size_t len, const string& str);
string& replace(size_t pos, size_t len, const char* s);
string& replace(size_t pos, size_t len, size_t n, char c);
使用:
cpp
string s1("hello world");
string s2("ABCD");
s1.replace(5, 1, s2);
cout << s1 << endl; //"helloABCDworld"
//replace也可以进行头插
s1.replace(0, 0, "aaa");
cout << s1 << endl; //"aaahelloABCDworld"
s1.replace(0, 8, 1, ' ');
cout << s1 << endl; //" ABCDworld"
swap:交换两个字符串对象的内容。
cpp
void swap(string& str);
使用:
cpp
string s1("hello world");
string s2("ABCD");
s1.swap(s2);
cout << s1 << endl; //"ABCD"
cout << s2 << endl; //"hello world"

【注意】swap正常来说需要1次拷贝构造,两次赋值重载,且都是深拷贝,为了减少这些消耗,编译器会自动使用交换两个对象的成员变量的方式完成内容的交换。
cpp
template <class T> void swap ( T& a, T& b )
{
T c(a); a=b; b=c;
}
pop_back(C++11):尾删一个字符。
cpp
void pop_back();
使用:
cpp
string s("hello world");
s.pop_back();
cout << s << endl; //"hello worl"
1.2.8 string类的操作函数(string operations)
c_str:返回C格式的常量字符串。
cpp
const char* c_str() const;
使用:
cpp
string s("hello world");
const char* str1 = s.c_str();
cout << str1 << endl; //"hello world"
string s2(str1);
cout << s2 << endl; //"hello world"
//无法使用非const字符串进行接收,返回的字符串无法进行修改,只能访问
//error C2440: "初始化": 无法从"const _Elem *"转换为"char *"
//char* str2 = s.c_str();
【注意】c_str返回的是常量字符串指针,只能进行访问,无法进行修改等操作。但可以进行拷贝等操作。
find :字符或字符串查找,查找成功返回首次出现的下标,查找失败返回string::npos 。默认从0下标位置开始查找。
cpp
//查找一个对象
size_t find(const string& str, size_t pos = 0) const;
//查找一段字符串
size_t find(const char* s, size_t pos = 0) const;
//从pos位置开始,查找一段字符串的前n个字符查找一段字符串的前n个字符
size_t find(const char* s, size_t pos, size_t n) const;
//查找一个字符
size_t find(char c, size_t pos = 0) const;
使用:
cpp
string s1("abcdefghijk");
string s2("hi");
string s3("ih");
size_t i1 = s1.find(s2);
size_t i2 = s1.find(s3);
cout << i1 << endl; //7
cout << i2 << endl; //4294967295(-1)
size_t i3 = s1.find("def");
cout << i3 << endl; //3
size_t i4 = s1.find("abds", 0, 2);
cout << i4 << endl; //0
string s4("hello world");
size_t i5 = s4.find(' ');
cout << i5 << endl; //5
【注意】查找字符串的前n个字符的find函数pos并没有给缺省值(为了避免和查找字符串调用时的歧义),需要自己手动传参。
rfind :反向开始查找字符或字符串第一次出现的位置,找到了返回反向第一次出现的下标 ,没找到返回string::npos。
cpp
//反向查找一个对象
size_t rfind(const string& str, size_t pos = npos) const;
//反向查找一段字符串
size_t rfind(const char* s, size_t pos = npos) const;
//从pos位置开始,反向查找一段字符串的前n个字符
size_t rfind(const char* s, size_t pos, size_t n) const;
//反向查找一个字符
size_t rfind(char c, size_t pos = npos) const;
使用:
cpp
string s1("hello world");
string s2("hello");
string s3("ehllo");
size_t i1 = s1.rfind(s2);
cout << i1 << endl; //0
size_t i2 = s1.rfind(s3);
cout << i2 << endl; //4294967295(-1)
size_t i3 = s1.rfind(" w");
cout << i3 << endl; //5
size_t i4 = s1.rfind('l');
cout << i4 << endl; //9
find_first_of :查找是否是子串里的任意一个字符,并返回第一次出现的下标,没有找到则返回string::npos。
cpp
size_t find_first_of(const string& str, size_t pos = 0) const;
size_t find_first_of(const char* str, size_t pos = 0) const;
size_t find_first_of(const char* str, size_t pos, size_t n) const;
size_t find_first_of(char c, size_t pos = 0) const;
使用:
cpp
string s1("hello world");
string s2("abcd");
size_t i1 = s1.find_first_of(s2);
cout << i1 << endl; //10,s1中的d和s2中字符串中的d匹配了,因此返回s1中d的下标
size_t i2 = s1.find_first_of("mnl");
cout << i2 << endl; //2,l匹配了,返回s1中第一个l出现的下标
size_t i3 = s1.find_first_of('c');
cout << i3 << endl; //4294967295,没找到返回size_t类型的-1
find_last_of:倒着查找是否是子串里的任意一个字符。
cpp
size_t find_last_of(const string& str, size_t pos = npos) const;
size_t find_last_of(const char* str, size_t pos = npos) const;
size_t find_last_of(const char* str, size_t pos, size_t n) const;
size_t find_last_of(char c, size_t pos = npos) const;
使用:
cpp
string s1("hello world");
string s2("abcd");
size_t i1 = s1.find_last_of(s2);
cout << i1 << endl; //10,s1中的d和s2中字符串中的d匹配了,因此返回s1中d的下标
size_t i2 = s1.find_last_of("mnl");
cout << i2 << endl; //9,l匹配了,返回s1中从后向前第一个l出现的下标
size_t i3 = s1.find_last_of('c');
cout << i3 << endl; //4294967295,没找到返回size_t类型的-1
find_first_not_of:正向查找第一个与子串任意字符都不匹配的字符。
cpp
size_t find_first_not_of(const string& str, size_t pos = 0) const;
size_t find_first_not_of(const char* str, size_t pos = 0) const;
size_t find_first_not_of(const char* str, size_t pos, size_t n) const;
size_t find_first_not_of(char c, size_t pos = 0) const;
使用:
cpp
string s1("hello world");
string s2("abcd");
size_t i1 = s1.find_first_not_of(s2);
cout << i1 << endl; //0
size_t i2 = s1.find_first_not_of("hello ");
cout << i2 << endl; //6
size_t i3 = s1.find_first_not_of('c');
cout << i3 << endl; //0
find_last_not_of:倒着查找不与子串任意一个字符匹配的字符。
cpp
size_t find_last_not_of(const string& str, size_t pos = 0) const;
size_t find_last_not_of(const char* str, size_t pos = 0) const;
size_t find_last_not_of(const char* str, size_t pos, size_t n) const;
size_t find_last_not_of(char c, size_t pos = 0) const;
使用:
cpp
string s1("hello world");
string s2("abcd");
size_t i1 = s1.find_last_not_of(s2);
cout << i1 << endl; //9
size_t i2 = s1.find_last_not_of("hello ");
cout << i2 << endl; //10
size_t i3 = s1.find_last_not_of('c');
cout << i3 << endl; //10
substr:从pos位置开始,获取len长度个字符,然后构造一个新的string对象,并返回。
cpp
string substr(size_t pos = 0, size_t len = npos) const;
使用:
cpp
string s1("hello world");
string s2 = s1.substr(0, 5);
cout << s2 << endl; //"hello"
string s3 = s1.substr(6);
cout << s3 << endl; //"world"
【注意】substr对于左闭右开区间例如**[i1,i2)** ,len的值就是i2-i1 ;如果是左开右开区间例如**(i1,i2)** ,len的值就是i2-i1-1 ;如果是左闭右闭区间例如**[i1,i2]** ,那么len的值就是i2-i1+1。
1.2.9 string类的非成员函数(Non-member function overload)
operator>>:流提取运算符重载。
cpp
istream& operator>>(istream& is, string& str);
使用:
cpp
string s;
cin >> s; //输入hello world
cout << s << endl; //"hello"
【注意】流提取运算符会自动以空格或换行进行分割,如果一行中存在空格等分隔符,那么流提取将无法提取一行的内容。
operator<<:流插入运算符重载。
cpp
ostream& operator>>(ostream& os, const string& str);
使用:
cpp
string s1("hello world");
cout << s1 << endl; //"hello world"
getline:获取一行字符串。
cpp
//读取以分割符结束的字符串
istream& getline(istream& is, string& str, char delim);
//读取一行字符串
istream& getline(istream& is, string& str);
使用:
cpp
string s;
getline(cin, s); //输入:hello world abc
cout << s << endl; //输出:hello world abc
getline(cin, s, 'c'); //输入:aaa
// bbb
// bcb
cout << s << endl; //输出:aaa
// bbb
// b
【注意】getline的默认分割符是'\n',能够完整的读取一整行的内容。同时也能自定义分隔符,可以用于读取多行内容。
relational operators:比较操作符。
C++并没有将string的相关比较操作符重载为成员函数,而是重载成了全局函数。C++中关于string的比较操作符有string和string之间的比较,有string和char*之间的比较。
cpp
bool operator==(const string& lhs, const string& rhs);
bool operator==(const char* lhs, const string& rhs);
bool operator==(const string& lhs, const char* rhs);
bool operator!=(const string & lhs, const string & rhs);
bool operator!=(const char* lhs, const string & rhs);
bool operator!=(const string & lhs, const char* rhs);
bool operator<(const string & lhs, const string & rhs);
bool operator<(const char* lhs, const string & rhs);
bool operator<(const string & lhs, const char* rhs);
bool operator<=(const string & lhs, const string & rhs);
bool operator<=(const char* lhs, const string & rhs);
bool operator<=(const string & lhs, const char* rhs);
bool operator>(const string & lhs, const string & rhs);
bool operator>(const char* lhs, const string & rhs);
bool operator>(const string & lhs, const char* rhs);
bool operator>=(const string & lhs, const string & rhs);
bool operator>=(const char* lhs, const string & rhs);
bool operator>=(const string & lhs, const char* rhs);
使用:
cpp
string s1("hello world");
string s2("zello world");
const char* s3 = "cello world";
cout << (s1 == s2) << endl; //0
cout << (s1 != s2) << endl; //1
cout << (s1 < s2) << endl; //1
cout << (s1 <= s2) << endl; //1
cout << (s1 > s2) << endl; //0
cout << (s1 >= s2) << endl; //0
cout << (s1 == s3) << endl; //0
cout << (s1 != s3) << endl; //1
cout << (s1 < s3) << endl; //0
cout << (s1 <= s3) << endl; //0
cout << (s1 > s3) << endl; //1
cout << (s1 >= s3) << endl; //1
二、string相关的一些操作
2.1 string的三种遍历方式
1.operator[]遍历
string重载的operator[]有const和非const两种版本,非const版本的operator[]能支持对string内容的读和写,const版本的operator[]只能读string的内容,无法对string的内容进行修改。
cpp
//非const对象调用非const版本operator[]
string s("hello world");
for (size_t i = 0; i < s.size(); i++)
{
s[i]++;
cout << s[i]; //"ifmmp!xpsme"
}
cout << endl;
//const对象调用const版本operator[]
const string s1("hello world");
for (size_t i = 0; i < s.size(); i++)
{
//error C3892: "s1": 不能给常量赋值
//s1[i]++;
cout << s1[i]; //"hello world"
}
cout << endl;
2.迭代器遍历
string的迭代器有begin、end、rbegin、rend、cbegin、cend、crbegin、crend。但begin、end、rbegin、rend里面本身就重载了const版本,因此cbegin、cend、crbegin、crend就显得比较多余。迭代器的类型一般比较长,但C++里有一个auto关键字,能自动识别类型。
cpp
//正向迭代器遍历
string s("hello world");
//auto关键字能自动识别类型
auto it = s.begin();
for (it; it < s.end(); it++)
{
//读和写
(*it)++;
cout << *it; //"ifmmp!xpsme"
}
cout << endl;
//反向迭代器遍历
string s1("hello world");
auto rit = s1.rbegin();
for (rit; rit < s1.rend(); rit++)
{
(*rit)++;
cout << *rit; //"emspx!pmmfi"
}
cout << endl;

【注意】反向迭代器遍历时rit也需要++才能向前走。
3.范围for遍历
for(auto 变量名 : 对象) 或for(auto& 变量名 : 对象)
范围for的底层和迭代器有关,且范围for只能正向遍历,无法反向遍历。
cpp
//只读遍历
string s("hello world");
for (auto e : s)
{
cout << e; //"hello world"
}
cout << endl;
cout << "s:" << s << endl; //"s:hello world"
for (auto e : s)
{
//e的++不会影响到s的内容,只会影响打印的内容
e++;
cout << e; //"ifmmp!xpsme"
}
cout << endl;
cout << "s:" << s << endl; //"s:hello world"
//可读可写遍历
for (auto& e : s)
{
cout << e; //"hello world"
}
cout << endl;
cout << "s:" << s << endl; //"s:hello world"
for (auto& e : s)
{
//再使用&后e的修改能够影响到s里的内容
e++;
cout << e; //"ifmmp!xpsme"
}
cout << endl;
cout << "s:" << s << endl; //"s:ifmmp!xpsme"
范围for也支持const string的遍历,但使用引用时只能进行读操作。
cpp
const string s1("hello world");
for (auto& e : s1)
{
//error C3892: "e": 不能给常量赋值
//e++;
cout << e;
}
cout << endl;
2.2 使用find、rfind、substr截取网站string的协议、域名和资源名
对于每个网站都会分为3个部分,协议、域名和资源名。且协议和域名不同的网站类型也会不同。
例如:https://legacy.cplusplus.com/reference/string/string/getline/
协议:https
资源名:reference/string/string/getline/
cpp
string s("https://legacy.cplusplus.com/reference/string/string/getline/");
//1.先用find查找':'用于截取协议
size_t i1 = s.find(':');
//substr的两个参数都是缺省参数,获取协议时两个参数都要传,[0,i1)
string s1 = s.substr(0, i1);
cout << s1 << endl; //"https"
//2.使用find从i1+3位置开始找第一个'/'用与获取域名
size_t i2 = s.find('/', i1 + 3);
//获取左闭右开区间的内容可以直接相减,[i1,i2)
string s2 = s.substr(i1 + 3, i2 - (i1 + 3));
cout << s2 << endl; //"legacy.cplusplus.com"
//3.从i2到剩下的全部内容就是资源名,[i2+1,s.size()-(i2+1))
string s3 = s.substr(i2 + 1, s.size() - (i2 + 1));
cout << s3 << endl; //"reference/string/string/getline/"
