目录
背景
在 C 语言中,字符类型是char
类型,字符串类型则是以\0
结尾的一些字符的集合,也就是char
类型数组,末尾自带\0
;而在 C 语言中,为了方便我们使用自己创建的字符串,因此在string.h
头文件中包含了许多字符串操作函数,但是 C++ 是面向对象编程的语言,而 C 语言中这样的操作很明显的将对象与行为分离了,不符合主思想;另外在使用这些函数时,参数什么的都需要用户自己管理,很容易造成访问越界,非常不友好;
因此在 C++ 中,在 STL 的容器中,出现了封装好的string
类,在该类中提供了很多接口供我们使用,使字符串的操作变得更加简单安全了,很好地实现了对象与行为的结合;
介绍
string
是表示字符串的字符串类;- 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作
string
的常规操作; string
在底层实际是:basic_string
模板类的实例化,typedef basic_string<char, char_traits, allocator> string;
;- 不能操作多字节或者变长字符的序列。
- 在使用
string
类时,必须包含#include<string>
头文件以及using namespace std;
;
常用接口
构造
string s;
:构造一个空的对象;
string s1; //空
string s(const char* s);
:用 C 风格的字符串构造一个对象;
string s2("hello"); //"hello"
string s = const char* s;
:单参构造的隐式类型转换,和上一个构造方式类似,用 C 风格字符串构造,这是上一个的简写形式;
string s2 = "hello";
string s(const string& str);
:用一个string
对象拷贝构造一个对象;
string s3(s2); //"hello"
string s(const string& str, size_t pos, size_t len = npos);
:从一个string
对象的 pos 位置开始,截取 len 个字符构造一个对象,第三个参数如果不传,则使用缺省值, 这个值的大小不要怕越界,内部会做处理;
string s4(s2, 2, 2); //"ll"
string s(const char* s, size_t n);
:将传入的 C 风格的字符串的前 n 个用来构造对象;
string s5("hello", 3); //"hel"
string s(size_t n, char c);
:用 n 个字符 从c 来构建一个对象;
string s6(5, 'a'); //"aaaaa"
- 模板迭代器:使用模板类,两个参数为另一个
string
对象的迭代器,注意范围为左闭右开;
template <class InputIterator>
string (InputIterator first, InputIterator last);
string s7 = (s6.begin(), s6.end); //"aaaaa"
赋值运算符
string& operator= (const char* s);
:使用 C 风格字符串给string
对象进行赋值;
string s1;
s1 = "hello:";
string& operator= (char c);
:使用单个字符给string
对象进行赋值;
string s2;
s2 = 'm';
string& operator= (const string& str);
:使用string
对象给string
对象进行赋值,该函数的返回值为string
对象的引用,因此赋值运算符可以连续调用,连续赋值;
string s3;
s3 = s2 = s1;
访问操作
[]
访问
char& operator[] (size_t pos);
:将string
对象的每个字符当做数组元素一样来进行访问,要注意下标不能越界,可读可写,在[]
中输入下标即可返回访问元素的引用;
string s1 = "hello";
s1[2] = 'a';
//输出 a
cout << s1[2] << endl;
const char& operator[] (size_t pos) const;
:将string
对象的每个字符当做数组元素一样来进行访问,要注意下标不能越界,只能进行读操作,不能进行写操作,并返回访问元素的引用,且被const
修饰,只读不可写;
const string s2 = "hello";
//下面代码错误,不可写
//s2[2] = 'a';
//输出 l,可读
cout << s2[2] << endl;
at
访问
char& at (size_t pos);
:输入下标,返回对应下标元素的引用,可读可写,要注意下标不能越界;
string s3 = "hello";
//可写
s3.at(2) = 'a';
//输出 a,可读
cout << s3.at(2) << endl;
const char& at (size_t pos) const;
:与const char& operator[] (size_t pos) const;
使用类似;
访问首尾元素
char& back();
:无参,访问string
对象的最后一个非\0
的字符,返回最后一个字符的引用值,因此可以修改此字符;
string s4 = "hello";
//访问最后一个字符,输出 o
cout << s4.back() << endl;
char& front();
:无参,访问string
对象的第一个的字符,返回第一个字符的引用值,因此可以修改此字符;
string s5 = "hello";
//访问第一个字符,输出 h
cout << s5.front() << endl;
- 注意:
- 当调用者为
const
修饰的对象时,那么就会自动调用对应的被const
修饰的函数,得到的返回值是被const
修饰的引用值,是不可以修改的; - 在使用
at
进行访问时总是做边界检查,而使用[]
进行访问时不做边界检查,因此使用[]
越界时会触发断言错误,使用at()
越界时会抛出异常;
- 当调用者为
迭代器
- 概念:迭代器的本质就是字符指针,是一个字符空间的地址,它是由
typedef type* iterator
形式定义的,也就是说他其实是指针的别名,注意在使用迭代器时,迭代的区间是左闭右开的,也就是说从左边的地址开始,直到右边地址的前一个结束; iterator begin();
:返回指向字符串的第一个字符的迭代器,可以使用iterator
类型变量接受,然后将解引用和 ++ 操作结合可以访问到每个字符;iterator end();
:返回指向字符串结束位置的后一个字符的迭代器,可以使用iterator
类型变量接受,被认为是正向迭代器遍历的结束符;
string str ("Test string");
for (string::iterator it = str.begin(); it != str.end(); ++it)
cout << *it; //"Test string"
reverse_iterator rbegin();
:返回指向字符串的最后一个字符的反向迭代器,可以使用iterator
类型变量接受,使用该迭代器进行遍历时,照样是执行 ++ 的操作才能从最后一个位置向前遍历,而不是想当然的执行 - - 操作,所以反向迭代器执行 ++ 操作,进行的是向前遍历的操作;reverse_iterator rend();
:返回指向string
对象的第一个字符之得前一个空间的反向迭代器,可以使用iterator
类型变量接受,被认为是反向迭代器遍历的结束符;
string str ("now step live...");
for(string::reverse_iterator rit = str.rbegin(); rit != str.rend(); ++rit)
cout << *rit; //"...evil pets won"
cbegin() / cend / crbegin / crend
:这四个函数的使用和上面四个一一对应,返回值类型是在上面的返回值类型前加const_
;这八个函数当操作const
修饰的对象时,他们都是一样的,都只有可读权限,没有可写权限,而当操作普通的string
对象时,上面的四个函数可以进行读写操作,而这四个函数只能执行只读操作,因为这四个函数为只读迭代器;
容量操作
有效长度
size_t size() const;
:以字节为单位返回字符串的长度,也就是有效长度;size_t length() const;
:以字节为单位返回字符串的长度,也就是有效长度;
容量大小
size_t capacity() const;
:获取当前为字符串分配的存储空间大小,也就是最大容量,以字节表示;size_t max_size() const;
:获取字符串可以达到的最大长度;
string s ("Test string");
cout << "The size of str is " << s.size() << " bytes." << endl;
cout << "The size of str is " << s.length() << " bytes." << endl;
cout << "The size of str is " << s.capacity() << " bytes." << endl;
cout << "The size of str is " << s.max_size() << " bytes." << endl;
修改有效长度
void resize (size_t n);
:将字符串的有效字符长度调整为 n 个字符,如果是增加有效字符的个数,那么新增的位置填充\0
,如果调整的大小大于最大容量,则编译器会自动增加最大容量来保证有效长度,如果是减小有效字符个数,那么将会减小 size 值;void resize (size_t n, char c);
:该函数和上面的接口功能相似,区别在于如果容量增加,那么则以指定字符来填充;
修改容量
void reserve (size_t n = 0);
:只能增加容量,将最大容量增加至指定大小;
string s ("Test string");
s.resize(15, 'a');
cout << s << endl << s.size() << endl << s.capacity() << endl;
s.resize(40);
s.reserve(60);
cout << s.size() << endl << s.capacity() << endl;
清空有效字符
void clear();
:清空有效字符,意味着将 size 置为 0;
判空
bool empty() const;
:判断字符串是否为空,返回布尔值,为空返回true
,不为空返回flase
;
调整大小
void shrink_to_fit();
:适当调整容量大小,使得容量与有效长度尽量保持在一个数量级上;
注意事项:
size()
与length()
方法底层实现原理完全相同,引入size()
的原因是为了与其他容器的接口保持一致,一般情况下基本都是用size()
;clear()
只是将string
中有效字符清空,不改变底层空间大小;resize
在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。reserve(size_t res_arg=0)
为string
预留空间,不改变有效元素个数,当reserve
的参数小于string
的底层空间总大小时,reserver
不会改变容量大小。- 一般情况下,我们在频繁增容时,使用
reserve
先增加到一个合适的大小,避免后续的频繁增容降低效率;
内容操作
+=
追加操作
string& operator+= (const string& str);
:在尾部追加string
对象;string& operator+= (const char* s);
:在尾部追加 C 风格字符串;string& operator+= (char c);
:在尾部追加单个字符;
string s1 = "abc";
string s2 = "123";
s2 += s1; //"123abc"
s2 += "def"; //"123abcdef"
s2 += 'g'; //"123abcdefg"
append
追加操作
string& append (const string& str);
:在当前值的末尾添加string
对象来扩展字符串;string& append (const string& str, size_t subpos, size_t sublen);
:在当前值的末尾添加string
对象的部分内容来扩展字符串,添加的内容为从string
对象的 subpos 位置开始添加 sublen 长度的字符内容;string& append (const char* s);
:在当前值的末尾添加 C 风格字符串来扩展字符串;string& append (const char* s, size_t n);
:在当前值的末尾添加 C 风格字符串的前 n 个字符来扩展字符串;string& append (size_t n, char c);
:在当前值的末尾添加 n 个指定字符来扩展字符串;- :在当前值的末尾添加
string
对象迭代器区间内的内容来扩展字符串,可以使用函数模板,所以也可以是 C 风格字符串的地址区间;
template <class InputIterator>
string& append (InputIterator first, InputIterator last);
string str;
string str2="Writing ";
string str3="print 10 and then 5 more";
str.append(str2); // 添加"Writing "
str.append(str3,6,3); // 添加"10 "
str.append("dots are cool",5); // 添加"dots "
str.append("here: "); // 添加"here: "
str.append(10u,'.'); // 添加".........."
str.append(str3.begin()+8,str3.end()); // 添加" and then 5 more"
尾插操作
void push_back (char c);
:尾插一个字符;
string s1 = "abc";
s1.push_back('d'); //"abcd"
assign
整体替换操作
string& assign (const string& str);
:将当前字符串内容替换为string
对象;string& assign (const string& str, size_t subpos, size_t sublen);
:将当前字符串内容替换为string
对象的部分内容,替换为从string
对象的 subpos 位置开始后续的 sublen 长度的内容;string& assign (const char* s);
:将当前字符串内容替换为指定 C 风格字符串;string& assign (const char* s, size_t n);
:将当前字符串内容替换为指定 C 风格字符串的前 n 个字符;string& assign (size_t n, char c);
:将当前字符串内容替换为 n 个指定字符;- 函数模板,将当前字符串内容替换为指定内容的某一区间的内容,可以是迭代器,也可以是地址空间;
template <class InputIterator>
string& assign (InputIterator first, InputIterator last);
string str;
string base="The quick brown fox jumps over a lazy dog.";
str.assign(base);
str.assign(base,10,9);// "brown fox"
str.assign("pangrams are cool",7);// "pangram"
str.assign("c-string");// "c-string"
str.assign(10,'*');// "**********"
str.assign(base.begin()+16,base.end()-12);// "fox jumps over"
insert
插入操作
string& insert (size_t pos, const string& str);
:在 pos 位置插入string
对象;string& insert (size_t pos, const string& str, size_t subpos, size_t sublen);
:在 pos 位置插入string
对象的部分内容,插入部分为string
对象的 subpos 位置开始后续的 sublen 长度的内容;string& insert (size_t pos, const char* s);
:在 pos 位置插入 C 风格的字符串;string& insert (size_t pos, const char* s, size_t n);
:在 pos 位置插入 C 风格的字符串的前 n 个字符;string& insert (size_t pos, size_t n, char c);
:在 pos 位置插入 n 个指定字符;void insert (iterator p, size_t n, char c);
:在迭代器 p 的位置插入 n 个指定字符;iterator insert (iterator p, char c);
:在迭代器 p 的位置插入一个指定字符;- 函数模板,在迭代器 p 的位置,插入另一个对象在某个区间的内容;
template <class InputIterator>
void insert (iterator p, InputIterator first, InputIterator last);
string str="to be question";
string str2="the ";
string str3="or not to be";
string::iterator it;
str.insert(6,str2); // to be (the )question
str.insert(6,str3,3,4); // to be (not )the question
str.insert(10,"that is cool",8); // to be not (that is )the question
str.insert(10,"to be "); // to be not (to be )that is the question
str.insert(15,1,':'); // to be not to be(:) that is the question
it = str.insert(str.begin()+5,','); // to be(,) not to be: that is the question
str.insert (str.end(),3,'.'); // to be, not to be: that is the question(...)
str.insert (it+2,str3.begin(),str3.begin()+3); // to be, (or) not to be: that is the question...
erase
删除操作
string& erase (size_t pos = 0, size_t len = npos);
:删除string
对象的某一下标范围内的字符内容;iterator erase (iterator p);
:删除string
对象的某一迭代器所指向的字符;iterator erase (iterator first, iterator last);
:删除string
对象的某一迭代器范围内的字符内容;
std::string str ("This is an example sentence.");// "This is an example sentence."
str.erase (10,8); // "This is an sentence."
str.erase (str.begin()+9); // "This is a sentence."
str.erase (str.begin()+5, str.end()-9); // "This sentence."
replace
部分替换操作
string& replace (size_t pos, size_t len, const string& str);
:将string
对象的某一下标区间内的内容替换为另一个string
对象;string& replace (iterator i1, iterator i2, const string& str);
:将string
对象的某一迭代器区间内的内容替换为另一个string
对象;string& replace (size_t pos, size_t len, const string& str, size_t subpos, size_t sublen);
:将string
对象的某一下标区间内的内容替换为另一个string
对象的某个下标区间内的内容;string& replace (size_t pos, size_t len, const char* s);
:将string
对象的某一下标区间内的内容替换为一个 C 风格字符串;string& replace (iterator i1, iterator i2, const char* s);
:将string
对象的某一迭代器区间内的内容替换为一个 C 风格字符串;string& replace (size_t pos, size_t len, const char* s, size_t n);
:将string
对象的某一下标区间内的内容替换为一个 C 风格字符串的前 n 个字符;string& replace (iterator i1, iterator i2, const char* s, size_t n);
:将string
对象的某一迭代器区间内的内容替换为一个 C 风格字符串的前 n 个字符;string& replace (size_t pos, size_t len, size_t n, char c);
:将string
对象的某一下标区间内的内容替换为 n 个指定字符;string& replace (iterator i1, iterator i2, size_t n, char c);
:将string
对象的某一迭代器区间内的内容替换为 n 个指定字符;- 函数模板,:将
string
对象的某一迭代器区间内的内容替换为某一字符指针范围内的内容;
template <class InputIterator>
string& replace (iterator i1, iterator i2, InputIterator first, InputIterator last);
string base="this is a test string.";
string str2="n example";
string str3="sample phrase";
string str4="useful.";
string str=base; // "this is a test string."
str.replace(9,5,str2); // "this is an example string." (1)
str.replace(19,6,str3,7,6); // "this is an example phrase." (2)
str.replace(8,10,"just a"); // "this is just a phrase." (3)
str.replace(8,6,"a shorty",7); // "this is a short phrase." (4)
str.replace(22,1,3,'!'); // "this is a short phrase!!!" (5)
str.replace(str.begin(),str.end()-3,str3); // "sample phrase!!!" (1)
str.replace(str.begin(),str.begin()+6,"replace"); // "replace phrase!!!" (3)
str.replace(str.begin()+8,str.begin()+14,"is coolness",7);// "replace is cool!!!" (4)
str.replace(str.begin()+12,str.end()-4,4,'o'); // "replace is cooool!!!" (5)
str.replace(str.begin()+11,str.end(),str4.begin(),str4.end());// "replace is useful." (6)
swap
交换成员函数
void swap (string& str);
:交换两个string
对象的内容;
string buyer ("money");
string seller ("goods");
cout << buyer << seller << '\n'; //"moneygoods"
seller.swap (buyer);
cout << buyer << seller << '\n';//"goodsmoney"
尾删操作
void pop_back();
:尾删string
对象的一个字符;
string str ("hello world!");
str.pop_back();
cout << str << '\n'; //"hello world"
取字符串空间地址
const char* c_str() const;
:返回string
类中的存放字符串空间的首地址,且以const
修饰;const char* data() const;
:返回string
类中的存放字符串空间的首地址,且以const
修饰;cout << str;
与cout << str.c_str();
与str.data();
虽然都是输出字符串,但是前者是输出所有有效字符,哪怕该有效字符是\0
,但是后两个则是遇到\0
就结束输出;
复制操作
size_t copy (char* s, size_t len, size_t pos = 0) const;
:将string
对象当前值的一个子字符串复制到 s 所指向的数组中。这个子字符串包含从pos位置开始的 len 个字符;
char buffer[20];
string str ("Test string...");
size_t length = str.copy(buffer,6,5);//"string"
find
正向查找
size_t find (const string& str, size_t pos = 0) const;
:从string
对象的 pos 位置开始向后查找另一个string
对象第一次出现的位置,如果查找到返回下标,未查找到则返回 npos;size_t find (const char* s, size_t pos = 0) const;
:从string
对象的 pos 位置开始向后查找一个 C 风格字符串第一次出现的位置,如果查找到返回下标,未查找到则返回 npos;size_t find (const char* s, size_t pos, size_t n) const;
:从string
对象的 pos 位置开始向后查找一个 C 风格字符串的前 n 个字符第一次出现的位置,如果查找到返回下标,未查找到则返回 npos;size_t find (char c, size_t pos = 0) const;
:从string
对象的 pos 位置开始向后查找一个字符第一次出现的位置,如果查找到返回下标,未查找到则返回 npos;
string str ("There are two needles in this haystack with needles.");
string str2 ("needle");
size_t found = str.find(str2); //14
found=str.find("needles are small",found+1,6); //44
found=str.find("haystack"); //30
found=str.find('.'); //51
rfind
反向查找
size_t rfind (const string& str, size_t pos = npos) const;
:从string
对象的 pos 位置开始向前查找另一个string
对象第一次出现的位置,如果查找到返回下标,未查找到则返回 npos;size_t rfind (const char* s, size_t pos = npos) const;
:从string
对象的 pos 位置开始向前查找一个 C 风格字符串第一次出现的位置,如果查找到返回下标,未查找到则返回 npos;size_t rfind (const char* s, size_t pos, size_t n) const;
:从string
对象的 pos 位置开始向前查找一个 C 风格字符串的前 n 个字符第一次出现的位置,如果查找到返回下标,未查找到则返回 npos;size_t rfind (char c, size_t pos = npos) const;
:从string
对象的 pos 位置开始向前查找一个字符第一次出现的位置,如果查找到返回下标,未查找到则返回 npos;
string str ("The sixth sick sheik's sixth sheep's sick.");
string key ("sixth");
size_t found = str.rfind(key);
str.replace (found,key.length(),"seventh");//"The sixth sick sheik's seventh sheep's sick."
(非)对象中的单个字符查找
size_t find_first_of (const string& str, size_t pos = 0) const;
:返回string
对象 str 中任意字符在string
对象中从 pos 位置向后的第一次出现位置;size_t find_first_of (const char* s, size_t pos = 0) const;
:返回 C 风格字符串中任意字符在string
对象中从 pos 位置向后的第一次出现位置;size_t find_first_of (const char* s, size_t pos, size_t n) const;
:返回 C 风格字符串前 n 个字符中的任意字符在string
对象中从 pos 位置向后的第一次出现位置;size_t find_first_of (char c, size_t pos = 0) const;
:返回某个指定字符在string
对象中从 pos 位置向后的第一次出现位置;find_last_not_of
:该函数的四种用法和上面的四个函数一一对应,不同的是参数 pos 的缺省值由 0 变为 npos,而且由从前向后找变为从后向前找;find_first_not_of
:该函数的四种用法和上面的四个函数一一对应,不同的是,功能从找属于第一个参数的任意字符第一次出现的位置变为找不属于第一个参数的任意字符第一次出现的位置;find_last_not_of
:该函数的四种用法和上面的四个函数一一对应,不同的是,功能从找属于第一个参数的任意字符从前向后第一次出现的位置变为找不属于第一个参数的任意字符从后向前第一次出现的位置,且参数 pos 由 0 变为 npos;
string str ("Please, replace the vowels in this sentence by asterisks.");
size_t found = str.find_first_of("aeiou");
while (found != string::npos){
str[found] = '*';
found = str.find_first_of("aeiou",found+1);
//Pl**s*, r*pl*c* th* v*w*ls *n th*s s*nt*nc* by *st*r*sks.
}
截取操作
string substr (size_t pos = 0, size_t len = npos) const;
:截取string
对象从 pos 位置开始的 len 个字符,然后用该部分内容创建一个新的string
对象,并返回该对象的值;
string str="We think in generalities, but we live in details.";
string str2 = str.substr (3,5); // "think"
compare
比较操作
- 比较函数:如果比较的两个字符串完全相等则返回 0;如果两个字符串中的第一个不匹配的字符的值在比较字符串中更低,或者所有匹配的字符都匹配但比较的字符串更短,则返回小于 0 的值;如果两个字符串中的第一个不匹配的字符的值在比较字符串中更大,或者所有匹配的字符都匹配但比较的字符串更长,则返回大于 0 的值;
int compare (const string& str) const;
:和string
对象比较大小;int compare (size_t pos, size_t len, const string& str) const;
:拿调用者的从 pos 位置开始的 len 个字符和string
对象比较大小;int compare (size_t pos, size_t len, const string& str, size_t subpos, size_t sublen) const;
:拿调用者的从 pos 位置开始的 len 个字符和string
对象的某一区间内的字符内容比较大小;int compare (const char* s) const;
:和 C 风格字符串比较大小;int compare (size_t pos, size_t len, const char* s) const;
:拿调用者的从 pos 位置开始的 len 个字符和和 C 风格字符串比较大小;int compare (size_t pos, size_t len, const char* s, size_t n) const;
:拿调用者的从 pos 位置开始的 len 个字符和和 C 风格字符串的前 n 个字符比较大小;
输入输出
operator>>
:输入运算符,使用此运算符进行输入时遇到空格或者换行则结束输入;istream& getline (istream& is, string& str);
:输入函数,第一个参数为cin
,将字符内容输入到string
对象中,遇到换行时结束输入;istream& getline (istream& is, string& str, char delim);
:输入函数,第一个参数为cin
,将字符内容输入到string
对象中,遇到 delim 字符或者换行时结束输入;operator<<
:输出运算符,将字符串打印在终端上;
string name;
cout << "Please, enter your full name: ";
getline (cin,name);
cout << "Hello, " << name << "!\n";
一些外部函数
operator+
:将两个string
对象拼接在一起,返回结果的副本,不建议使用,尽量使用operator+=
;swap()
:外部交换函数;