主页 > 其他  > 

string类详解(上)

string类详解(上)

文章目录 目录1. STL简介1.1 什么是STL1.2 STL的版本1.3 STL的六大组件 2. 为什么学习string类3. 标准库中的string类3.1 string类3.2 string类的常用接口说明

目录 STL简介为什么学习string类标准库中的string类 1. STL简介 1.1 什么是STL

STL(standard template libaray-标准模板库):是C++标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架。

1.2 STL的版本 原始版本

Alexander Stepanov、Meng Lee 在惠普实验室完成的原始版本,本着开源精神,他们声明允许任何人任意运用、拷贝、修改、传播、商业使用这些代码,无需付费。唯一的条件就是也需要向原始版本一样做开源使用。HP 版本–所有STL实现版本的始祖。

P. J. 版本

由P. J. Plauger开发,继承自HP版本,被Windows Visual C++采用,不能公开或修改,缺陷:可读性比较低,符号命名比较怪异。

RW版本

由Rouge Wage公司开发,继承自HP版本,被C+ + Builder 采用,不能公开或修改,可读性一般。

SGI版本

由Silicon Graphics Computer Systems,Inc公司开发,继承自HP版本。被GCC(Linux)采用,可移植性好,可公开、修改甚至贩卖,从命名风格和编程风格上看,阅读性非常高。我们后面学习STL要阅读部分源代码,主要参考的就是这个版本。

1.3 STL的六大组件

2. 为什么学习string类

C语言中,字符串是以’\0’结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。

3. 标准库中的string类 3.1 string类

string类的具体信息可以通过cplusplus网站进行查阅

在使用string类时,必须包含#include头文件以及using namespace std;

3.2 string类的常用接口说明

我们先来总的看一下string类的常用接口:

string类对象的常见构造 string类对象的容量操作 string类对象的访问及遍历操作 string类对象的修改操作 string类非成员函数

接下来,我们通过一些具体的场景来学习如何使用这些接口:

如何构造一个string类对象 #include <iostream> #include <string> using namespace std; void test_string1() { //常用 string s1; string s2("hello world"); string s3(s2); //不常用 了解 string s4(s2, 3, 5); string s5(s2, 3); string s6(s2, 3, 30); string s7("hello world", 5); string s8(10, 'x'); cout << s1 << endl; cout << s2 << endl; cout << s3 << endl; cout << s4 << endl; cout << s5 << endl; cout << s6 << endl; cout << s7 << endl; cout << s8 << endl; cin >> s1; cout << s1 << endl; } int main() { test_string1(); return 0; }

补充:

void push_back(const string& s) { } void test_string2() { //构造 string s1("hello world"); //隐式类型转换 string s2 = "hello world"; const string& s3 = "hello world"; push_back(s1); push_back("hello world"); } int main() { test_string2(); return 0; }
string的遍历

第一种方法:

//class string //{ //public: // //引用返回 // //1. 减少拷贝 // //2. 修改返回对象 // char& operator[](size_t i) // { // assert(i < _size); // // return _str[i]; // } //private: // char* _str; // size_t _size; // size_t _capacity; //}; void test_string3() { string s1("hello world"); cout << s1.size() << endl;//11 //cout << s1.length() << endl;//11 for (size_t i = 0; i < s1.size(); i++) { s1[i]++; } s1[0] = 'x'; //越界检查 //s1[20]; for (size_t i = 0; i < s1.size(); i++) { //cout << s1.operator[](i) << " "; cout << s1[i] << " "; } cout << endl; const string s2("hello world"); //不能修改 //s2[0] = 'x'; } int main() { test_string3(); return 0; }

注: size() 与 length() 方法底层实现原理完全相同,引入 size() 的原因是为了与其他容器的接口保持一致,一般情况下基本都是用 size() 。

第二种方法:

void test_string4() { string s1("hello world"); //遍历方式2:迭代器 string::iterator it1 = s1.begin(); while (it1 != s1.end()) { *it1 += 3; cout << *it1 << " "; ++it1; } cout << endl; //cout << typeid(it1).name() << endl; } int main() { test_string4(); return 0; }

void test_string4() { list<int> lt1; lt1.push_back(1); lt1.push_back(2); lt1.push_back(3); list<int>::iterator it = lt1.begin(); while (it != lt1.end()) { cout << *it << " "; ++it; } cout << endl; } int main() { test_string4(); return 0; }

第三种方法:

void test_string4() { string s1("hello world"); //遍历方式3:范围for(通用的) //底层角度,它就是迭代器 for (auto& e : s1) { e++;//不会影响s1中的数据,它是一个赋值拷贝;要加上引用才会改变s1中的数据 cout << e << " "; } cout << endl; cout << s1 << endl; list<int> lt1; lt1.push_back(1); lt1.push_back(2); lt1.push_back(3); for (auto& e : lt1) { cout << e << " "; } cout << endl; } int main() { test_string4(); return 0; }

注: 除了普通迭代器之外,还有

const迭代器 void test_string5() { const string s1("hello world"); //string::const_iterator it1 = s1.begin(); auto it1 = s1.begin(); while (it1 != s1.end()) { //不能修改 //*it1 += 3; cout << *it1 << " "; ++it1; } cout << endl; } int main() { test_string5(); return 0; } 反向迭代器 void test_string5() { string s2("hello world"); string::reverse_iterator it2 = s2.rbegin(); //auto it2 = s2.rbegin(); while (it2 != s2.rend()) { *it2 += 3; cout << *it2 << " "; ++it2; } cout << endl; const string s3("hello world"); string::const_reverse_iterator it3 = s3.rbegin(); //auto it3 = s3.rbegin(); while (it3 != s3.rend()) { //不能修改 //*it3 += 3; cout << *it3 << " "; ++it3; } cout << endl; } int main() { test_string5(); return 0; }
按字典序排序 #include <algorithm> void test_string6() { string s1("hello world"); cout << s1 << endl; //s1按字典序(ASCII码)排序 //sort(s1.begin(), s1.end()); //第一个和最后一个不参与排序 //sort(++s1.begin(), --s1.end()); //前5个排序 sort(s1.begin(), s1.begin() + 5); cout << s1 << endl; } int main() { test_string6(); return 0; } 插入字符 void test_string7() { string s1("hello world"); cout << s1 << endl; s1.push_back('x'); cout << s1 << endl; s1.append(" yyyyyy!!"); cout << s1 << endl; string s2("111111"); s1 += 'y'; s1 += "zzzzzzzz"; s1 += s2; cout << s1 << endl; } int main() { test_string7(); return 0; }

注: 在string尾部追加字符时,s.push_back(‘c’) / s.append(1, ‘c’) / s += ‘c’ 三种的实现方式差不多,一般情况下string类的 += 操作用的比较多,+= 操作不仅可以连接单个字符,还可以连接字符串。

关于修改的一些接口 void test_string8() { string s1("hello world"); cout << s1 << endl; s1.assign("111111"); cout << s1 << endl; //慎用,因为效率不高 -> O(N) //实践中需求也不高 string s2("hello world"); s2.insert(0, "xxxx"); cout << s2 << endl; s2.insert(0, 1, 'y'); cout << s2 << endl; s2.insert(s2.begin(), 'y'); cout << s2 << endl; s2.insert(s2.begin(), s1.begin(), s1.end()); cout << s2 << endl; } int main() { test_string8(); return 0; } void test_string9() { string s1("hello world"); cout << s1 << endl; //erase效率不高,慎用,和insert类似,要挪动数据 s1.erase(0, 1); cout << s1 << endl; //s1.erase(5); s1.erase(5, 100); cout << s1 << endl; //replace效率不高,慎用,和insert类似,要挪动数据 string s2("hello world"); s2.replace(5, 1, "%20"); cout << s2 << endl; string s3("hello world hello bit"); for (size_t i = 0; i < s3.size(); ) { if (' ' == s3[i]) { s3.replace(i, 1, "%20"); i += 3; } else { i++; } } cout << s3 << endl; string s4("hello world hello bit"); string s5; for (auto ch : s4) { if (ch != ' ') { s5 += ch; } else { s5 += "%20"; } } cout << s5 << endl; } int main() { test_string9(); return 0; }

我们来做几个题目:

仅仅反转字母 class Solution { public: bool isLetter(char ch) { if (ch >= 'a' && ch <= 'z') { return true; } if (ch >= 'A' && ch <= 'Z') { return true; } return false; } string reverseOnlyLetters(string s) { if (s.empty()) { return s; } size_t begin = 0, end = s.size() - 1; while (begin < end) { while (begin < end && !isLetter(s[begin])) { ++begin; } while (begin < end && !isLetter(s[end])) { --end; } swap(s[begin], s[end]); ++begin; --end; } return s; } }; 字符串中的第一个唯一字符 class Solution { public: int firstUniqChar(string s) { int count[26] = { 0 }; //统计次数 for (auto ch : s) { count[ch - 'a']++; } for (size_t i = 0; i < s.size(); ++i) { if (1 == count[s[i] - 'a']) { return i; } } return -1; } }; 验证回文串 class Solution { public: bool isLetterOrNumber(char ch) { return (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z'); } bool isPalindrome(string s) { for (auto& ch : s) { if (ch >= 'A' && ch <= 'Z') { ch += 32; } } int begin = 0, end = s.size() - 1; while (begin < end) { while (begin < end && !isLetterOrNumber(s[begin])) { ++begin; } while (begin < end && !isLetterOrNumber(s[end])) { --end; } if (s[begin] != s[end]) { return false; } else { ++begin; --end; } } return true; } }; 字符串相加

法一:

//时间复杂度:O(N^2) 因为头插的效率太低 class Solution { public: string addStrings(string num1, string num2) { int end1 = num1.size() - 1; int end2 = num2.size() - 1; string str; int next = 0;//进位 while (end1 >= 0 || end2 >= 0) { int x1 = end1 >= 0 ? num1[end1--] - '0': 0; int x2 = end2 >= 0 ? num2[end2--] - '0': 0; int x = x1 + x2 + next; //处理进位 next = x / 10; x = x % 10; //头插 //str.insert(0, 1, '0' + x); str.insert(str.begin(), '0' + x); } if (1 == next) { str.insert(str.begin(), '1'); } return str; } };

法二:

//时间复杂度:O(N) class Solution { public: string addStrings(string num1, string num2) { int end1 = num1.size() - 1; int end2 = num2.size() - 1; string str; int next = 0;//进位 while (end1 >= 0 || end2 >= 0) { int x1 = end1 >= 0 ? num1[end1--] - '0': 0; int x2 = end2 >= 0 ? num2[end2--] - '0': 0; int x = x1 + x2 + next; //处理进位 next = x / 10; x = x % 10; //尾插 str += ('0' + x); } if (1 == next) { str += '1'; } reverse(str.begin(), str.end()); return str; } };
string类对象的容量操作 void TestPushBack() { string s; size_t sz = s.capacity(); cout << "capacity changed: " << sz << '\n'; cout << "making s grow:\n"; for (int i = 0; i < 200; ++i) { s.push_back('c'); if (sz != s.capacity()) { sz = s.capacity(); cout << "capacity changed: " << sz << '\n'; } } } void test_string10() { string s1("hello world hello bit"); cout << s1.size() << endl; cout << s1.capacity() << endl; cout << s1.max_size() << endl; TestPushBack(); string s1("111111111"); string s2("11111111111111111111111111111111111111111111111111"); } int main() { test_string10(); return 0; }

void TestPushBack() { string s; //知道需要多少空间,提前开好 s.reserve(200); size_t sz = s.capacity(); cout << "capacity changed: " << sz << '\n'; cout << "making s grow:\n"; for (int i = 0; i < 200; ++i) { s.push_back('c'); if (sz != s.capacity()) { sz = s.capacity(); cout << "capacity changed: " << sz << '\n'; } } } void test_string10() { TestPushBack(); string s1("111111111"); string s2("11111111111111111111111111111111111111111111111111"); cout << s1.capacity() << endl; s1.reserve(100); cout << s1.capacity() << endl; s1.reserve(20); cout << s1.capacity() << endl; } int main() { test_string10(); return 0; }

void test_string11() { string s1; //s1.resize(5, '0'); s1.resize(5); s1[4] = '3'; s1[3] = '4'; s1[2] = '5'; s1[1] = '6'; s1[0] = '7'; //76543 //插入(空间不够会扩容) string s2("hello world"); s2.resize(20, 'x'); //删除 s2.resize(5); //s2[10]; try { s2.at(10); } catch (const exception& e) { cout << e.what() << endl; } } int main() { test_string11(); return 0; }

注:

clear()只是将string中有效字符清空,不改变底层空间大小。(代码中没有演示)resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于string的底层空间总大小时,reserver不会改变容量大小。对string操作时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留好。
string类的一些其他操作 #define _CRT_SECURE_NO_WARNINGS 1 void test_string12() { string file("test.cpp"); FILE* fout = fopen(file.c_str(), "r"); char ch = fgetc(fout); while (ch != EOF) { cout << ch; ch = fgetc(fout); } } int main() { test_string12(); return 0; } void test_string12() { string file("string.cpp.zip"); size_t pos = file.rfind('.'); //string suffix = file.substr(pos, file.size() - pos); string suffix = file.substr(pos); cout << suffix << endl; } int main() { test_string12(); return 0; } void test_string12() { string url(" gitee /ailiangshilove/cpp-class/blob/master/%E8%AF%BE%E4%BB%B6%E4%BB%A3%E7%A0%81/C++%E8%AF%BE%E4%BB%B6V6/string%E7%9A%84%E6%8E%A5%E5%8F%A3%E6%B5%8B%E8%AF%95%E5%8F%8A%E4%BD%BF%E7%94%A8/TestString.cpp"); size_t pos1 = url.find(':'); string url1 = url.substr(0, pos1 - 0); cout << url1 << endl; size_t pos2 = url.find('/', pos1 + 3); string url2 = url.substr(pos1 + 3, pos2 - (pos1 + 3)); cout << url2 << endl; string url3 = url.substr(pos2 + 1); cout << url3 << endl; } int main() { test_string12(); return 0; } void test_string13() { 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); } cout << str << '\n'; } int main() { test_string13(); return 0; } void SplitFilename(const string& str) { cout << "Splitting: " << str << '\n'; size_t found = str.find_last_of("/\\"); cout << " path: " << str.substr(0, found) << '\n'; cout << " file: " << str.substr(found + 1) << '\n'; } int main() { string str1("/usr/bin/man"); string str2("c:\\windows\\winhelp.exe"); SplitFilename(str1); SplitFilename(str2); return 0; } void test_string14() { string s1 = "hello"; string s2 = "world"; string ret1 = s1 + s2; cout << ret1 << endl; string ret2 = s1 + "xxxxx"; cout << ret2 << endl; string ret3 = "xxxxx" + s1; cout << ret3 << endl; //字典序比较 cout << (s1 < s2) << endl; } int main() { test_string14(); return 0; }

一个题目:

字符串最后一个单词的长度

#include <iostream> using namespace std; int main() { string str; //默认规定空格或者换行是多个值之间分割 //cin >> str; //获取一行中包含空格,不能用>> getline(cin, str); size_t pos = str.rfind(' '); cout << str.size() - (pos + 1) << endl; return 0; }
输入多行字符依次打印: int main() { //默认规定空格或者换行是多个值之间分割 string str; //ctrl + z 就可以结束 while (cin >> str) { cout << str << endl; } return 0; } 字符串转整形,整形转字符串 int main() { //atoi itoa //to_string int x = 0, y = 0; cin >> x >> y; string str = to_string(x + y); cout << str << endl; int z = stoi(str); return 0; }
标签:

string类详解(上)由讯客互联其他栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“string类详解(上)