11.剪切(substr)
string str = "The apple thinks apple is delicious"; //s.substr(pos,n) 得到字符串s位置为pos后面的n个字符组成的串 string s1 = str.substr(4, 5); // s1 = "apple" //s.substr(pos) 得到字符串s从pos到结尾的串 string s2 = str.substr(17); // s2 = "apple is delicious" string s3 = str.substr(4, 100); // s3 = "apple thinks apple is delicious" string s4 = str.substr(4, string::npos);// s4 = "apple thinks apple is delicious"
12.比较(compare)
两个字符串自左向右逐个字符相比(按ASCII值大小相比较),直到出现不同的字符或遇’\0’为止。若是遇到‘\0’结束比较,则长的子串大于短的子串,如:“9856” > “985”。如果两个字符串相等,那么返回0,调用对象大于参数返回1,小于返回-1。
string str1 = "small leaf"; string str2 = "big leaf"; //s.compare(str) 比较当前字符串s和str的大小 cout << str1.compare(str2); // 1 //s.compare(pos,n,str) 比较当前字符串s从pos开始的n个字符与str的大小 cout << str1.compare(2, 7, str2); // -1 //s.compare(pos,n0,str,pos2,n) 比较当前字符串s从pos开始的n0个字符与str中pos2开始的n个字符组成的字符串的大小 cout << str1.compare(6, 4, str2, 4, 4); // 0 //s.compare(pos,n0,cstr,n) 比较当前字符串s从pos开始的n0个字符与字符数组cstr中前n个字符的大小 //此处不可将"big leaf"替换为str2 cout << str1.compare(6, 4, "big leaf", 4); // 1
13.交换(swap)
string str1 = "small leaf"; string str2 = "big leaf"; //或者str1.swap(str2) ,输出结果相同 swap(str1,str2); // str1 = "big leaf" str2 = "small leaf" swap(str1[0],str1[1]); // str1 = "ibg leaf"
14.反转(reverse)
反转字符串。
string str = "abcdefghijklmn"; string::iterator it1 = str.begin(); string::iterator it2 = str.end(); reverse(str.begin(), str.end()); // str = "nmlkjihgfedcba" reverse(it1, it2); // 又反转了一次:str = "abcdefghijklmn"
对于str.begin()和str.end()类型,其返回值可以看成指针,但实际上并不是指针,因此我们用char* 定义的it1和it2是不对的,其在不同的平台上有不同的含义,事实上其有着迭代器的作用。(下面讲解迭代器的使用)
15. 迭代器(iterator)
迭代器实际上是一个像指针一样的东西,这是对行为来说的。需要注意的是,这里不能用char*,虽然对于vs这个平台可以使用char*,但难保其他的平台是char*,因此迭代器的底层不一定是char*,这在模拟实现中将会详细介绍。(对于迭代器来说,只要会用string类,那么vector、list等容器与其方法是一样的)
15.1正向迭代器
//迭代器 -- 通用的访问形式 string s1("1234"); string::iterator it1 = s1.begin();//s.begin() 返回字符串s第一个字符的位置 while (it1 != s1.end())//s.end() 返回字符串s最后一个字符串的后一个位置 { *it1 += 1; ++it1; } it1 = s1.begin(); while (it1 != s1.end()) { cout << *it1 << " "; ++it1; } // 输出:2 3 4 5
15.2反向迭代器
当然,还有反向迭代器,其访问顺序与默认的迭代器相反。
string s1("1234"); string::reverse_iterator rit = s1.rbegin(); while (rit != s1.rend()) { cout << *rit << " "; ++rit; } // 输出: 4 3 2 1
此外,我们发现此定义名过长,因此我们也可以用auto去定义变量rit接收rbegin()。
15.3const迭代器
当我们需要只读的时候,为了避免改变其中的值,在迭代器使用时我们就会选择const迭代器,顾名思义const迭代器能够保护迭代指向的变量不被改变,那我们实际来看一下const迭代器如何使用:
void Print(const string& s) { string::const_iterator it = s.begin();//正向 while (it != s.end()) { cout << *it << " "; it++; } cout << endl; string::const_reverse_iterator rit = s.rbegin(); //反向 可以用auto代替,即:auto rit = s.rbegin(); while (rit != s.rend()) { cout << *rit << " "; rit++; } cout << endl; } int main() { string s1("1234"); Print(s1); return 0; }
总结:迭代器经过上述的描述,一共有四种,即正向、反向、正向const、反向const,其不能混合定义,否则会出现错误。
16.搜索与查找(find等函数)
16.1 find()函数
string str = "The apple thinks apple is delicious"; //长度34 string key = "apple"; //s.find(str) 查找字符串str在当前字符串s中第一次出现的位置 int pos1 = str.find(key); // 4 //s.find(str,pos) 查找字符串str在当前字符串s的[pos,end]中第一次出现的位置 int pos2 = str.find(key, 10); // 17 //s.find(cstr,pos,n) 查找字符数组cstr前n的字符在当前字符串s的[pos,end]中第一次出现的位置 //此处不可将"delete"替换为str2(如果定义str2 = "delete") int pos3 = str.find("delete", 0, 2); // 26 //s.find(ch,pos) 查找字符ch在当前字符串s的[pos,end]中第一次出现的位置 int pos4 = str.find('s', 0); // 15
16.2 rfind函数
rfind与find的区别就是:find是从左往右找(即从前往后),而rfind是从后往前找。
string str = "The apple thinks apple is delicious"; //长度34 string key = "apple"; //s.rfind(str) 查找字符串str在当前字符串s中最后一次出现的位置 int pos5 = str.rfind(key); // 17 //s.rfind(str,pos) 查找字符串str在当前字符串s的[0,pos+str.length()-1]中最后一次出现的位置 int pos6 = str.rfind(key, 16); // 4 //s.rfind(cstr,pos,n) 查找字符数组cstr前n的字符在当前字符串s的[0,pos+n-1]中最后一次出现的位置 //此处不可将"apple"替换为key int pos7 = str.rfind("apple", 40, 2); // 17 //s.rfind(ch.pos) 查找字符ch在当前字符串s的[0,pos]中最后一次出现的位置 int pos8 = str.rfind('s', 30); // 24
16.3 find_xxx_of()函数(功能强大,但不常用)
xxx是需要确定的名字,因此下面将介绍这种类型的查找函数:(4个)
// s.find_first_of(str) 查找字符串str中的任意字符在当前字符串s中第一次出现的位置 int pos1 = str.find_first_of(key); // 2 //s.find_first_of(str,pos) 查找字符串str中的任意字符在当前字符串s的[pos,end]中第一次出现的位置 int pos2 = str.find_first_of(key, 10); // 11 //s.find_first_of(cstr,pos,n) 查找字符串str前n个任意字符在当前字符串s的[pos,end]中第一次出现的位置 //此处不可将"aeiou"替换为key int pos3 = str.find_first_of("aeiou", 7, 2); // 17 //s.find_first_of(ch,pos) 查找字符ch在当前字符串s的[pos,end]中第一次出现的位置 int pos4 = str.find_first_of('r', 0); // 6
最好的解释就是举例子:
即找到每个str中的字符进行替换,与find和rfind的区别是:此查找找的是字符串中的所有字符,而不是字符串。下面的几个同样如此:
//s.find_last_of(str) 查找字符串str中的任意字符在当前字符串s中最后一次出现的位置 int pos1 = str.find_last_of(key); // 27 //s.find_last_of(str,pos) 查找字符串str中的任意字符在当前字符串s的[0,pos]中最后一次出现的位置 int pos2 = str.find_last_of(key, 15); // 11 //s.find_last_of(cstr,pos,n) 查找字符串str前n个任意字符在当前字符串s的[0,pos]中最后一次出现的位置 //此处不可将"aeiou"替换为key int pos3 = str.find_last_of("aeiou", 20, 2); // 17 //s.find_last_of(str) 查找字符ch在当前字符串s的[0,pos]中最后一次出现的位置 int pos4 = str.find_last_of('r', 30); // 28
//s.find_first_not_of(str) 查找字符串str之外的任意字符在当前字符串s中第一次出现的位置 int pos1 = str.find_first_not_of(key); // 0 //s.find_first_not_of(str,pos) 查找字符串str之外的任意字符在当前字符串s的[pos,end]中第一次出现的位置 int pos2 = str.find_first_not_of(key, 10); // 10 //s.find_first_not_of(cstr,pos,n) 查找字符串str前n个之外任意字符在当前字符串s的[pos,end]中第一次出现的位置 //此处不可将"aeiou"替换为key int pos3 = str.find_first_not_of("aeiou", 7, 2); // 7 //s.find_first_not_of(str) 查找字符ch之外任意字符在当前字符串s的[pos,end]中第一次出现的位置 int pos4 = str.find_first_not_of('r', 0); // 0
//s.find_last_not_of(str) 查找字符串str之外的任意字符在当前字符串s中最后一次出现的位置 int pos1 = str.find_last_not_of(key); // 29 //s.find_last_not_of(str,pos) 查找字符串str之外的任意字符在当前字符串s的[0,pos]中最后一次出现的位置 int pos2 = str.find_last_not_of(key, 15); // 15 //s.find_last_not_of(cstr,pos,n) 查找字符串str前n个之外任意字符在当前字符串s的[0,pos]中最后一次出现的位置 //此处不可将"aeiou"替换为key int pos3 = str.find_last_not_of("aeiou", 20, 2); // 20 //s.find_last_not_of(str) 查找字符ch之外任意字符在当前字符串s的[0,pos]中最后一次出现的位置 int pos4 = str.find_last_not_of('r', 30); // 29
3.string类的应用
3.1 三种遍历方式
int main() { string s1("1234"); // 遍历他 // 1、下标 [] for (size_t i = 0; i < s1.size(); ++i) { s1[i]++;//实际上这是operator[]()的运算符重载 } //s1[10]; cout << s1 << endl; // 2、范围for for (auto& ch : s1) { ch--; } cout << s1 << endl; // 反转一下 size_t begin = 0, end = s1.size() - 1; while (begin < end) { swap(s1[begin++], s1[end--]); } cout << s1 << endl; //reverse(s1.begin(), s1.end()); 算法头文件中的函数,直接使用即可。 cout << s1 << endl; // 3、迭代器 -- 通用的访问形式 string::iterator it1 = s1.begin(); while (it1 != s1.end()) { *it1 += 1; ++it1; } it1 = s1.begin(); while (it1 != s1.end()) { cout << *it1 << " "; ++it1; } cout << endl; }
3.2替换空格
解法1:
对于这道题,如果按照c语言的方式会很麻烦,但是通过C++string中的函数,我们可以先find,再replace:
class Solution { public: string replaceSpace(string s) { size_t pos = s.find(' '); while(pos != string::npos) { s.replace(pos, 1, "%20"); pos = s.find(' ',pos+3);//+3是优化 } return s; } };
+3实际上就是对代码的优化,因为%20就对应了3个位置,因此+3跳过这三个字符查找的效率更快。
解法2: 我们还可以新建一个string的空字符串,遍历传入的string s如果没有碰到空格就+=该字符,碰到了空格就+=%20
class Solution { public: string replaceSpace(string s) { string ret; ret.reserve(s.size()*3);// 优化,提前开好空间,省去后续扩容的消耗 for(auto ch : s) { if(ch != ' ') { ret += ch; } else { ret += "%20"; } } return ret; } };
ret.reserve(s.size()*3);
是面对全是空格的情况,这样的极端情况就可以作为扩容的标准。这样我们发现,以空间换时间的做法就可以有效的提高效率。
3.3通过find取后缀
如果我们想确定一个文件是什么类型,我们需要知道其后缀,最后一个.后面的名字就是其后缀,那我们就可以先通过rfind()找到最后一个.的位置,再通过substr拷贝下来,这样就获得了相应文件的后缀了。(找最后一个.是由于在Linux下的文件可能存在类似于test.cpp.zip.tar的文件名,而其文件类型其实是tar)
void test_string11() { string file; cin >> file; size_t pos = file.rfind('.'); if (pos != string::npos) { string suffix = file.substr(pos); cout << suffix << endl; } }
3.4 getline的应用
将getline放在这里是因为这里直接利用会更具体的描述。题目:
HJ1 字符串最后一个单词的长度
这道题如果我们直接用scanf或者cin的话,对于输入hello nowcoder来说,实际上输入的只有hello,因为到空格就截止了,因此这里就需要了getline输入,getline的作用是到换行符时才结束输入。那我们看看代码实现:
#include <iostream> #include<string> using namespace std; int main() { string s; getline(cin , s); size_t pos = s.rfind(' '); cout << s.size() - pos - 1 <<endl; return 0; }
4. string总结
本文详细介绍了string中各个函数功能以及接口,通过这些的灵活运用,才能更好的掌握C++,而对于这些函数的底层实现,下一篇将用的模拟实现string加深对各种函数的理解。