目录
一、为什么学习string
二、标准库中的string类
2.1 string类的简介
2.2 成员类型
2.3 成员函数
2.3.1 构造、析构与运算符重载
2.3.2 迭代器
2.3.3 容量
2.3.4 元素的存取
2.3.5 修改
2.3.6 字符串操作
2.4 成员常量
2.5 非成员函数重载
三、string编程题练习
1. 仅仅反转字母
2. 字符串最后一个单词的长度
3. 字符串相加
4. 验证回文串
四、string类的模拟实现
一、为什么学习string
string的诞生略早于STL(STL(standard template libaray-标准模板库):是C++标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架),与STL里面的vector、list相比存在一些不成熟的地方。string与STL里面的vector、list具有相似之处,但是string是被包含在标准库中的。在接下来的学习过程中,我们务必需要阅读一些文档,可能需要一点点英语的基础(尽量不去使用翻译,因为翻译不一定能精确地表达出原意,理解的些许偏差可能对你的学习产生不好的影响,遇到陌生的单词搜索一下它的中文意思即可)
string - C++ Reference (cplusplus.com)https://legacy.cplusplus.com/reference/string/string/?kw=string C语言中,字符串是以'\0'结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,不太符合OOP(面向对象编程)的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。学习string可以更好地对字符串整体进行操作,而不是像C语言那样对字符串的字符进行操作。
二、标准库中的string类
注意:2.1 ~ 2.5 中并不会详细介绍每一个函数,仅介绍部分常用函数的常规用法(并且以代码运行的截图搭配文字说明展现出来),具体的内容请以文档为准,希望大家能够通过阅读文档学到需要的知识并提升自己相应的能力
2.1 string类的简介
2.2 成员类型
2.3 成员函数
2.3.1 构造、析构与运算符重载
2.3.2 迭代器
2.3.3 容量
2.3.4 元素的存取
2.3.5 修改
2.3.6 字符串操作
2.4 成员常量
2.5 非成员函数重载
string设计了近百个函数,相对有些冗余,学习的时候掌握一些常用的函数(你在学习或者刷题的时候经常遇到的),其它的在需要的时候查看文档即可
三、string编程题练习
1. 仅仅反转字母
917. 仅仅反转字母 - 力扣(LeetCode)
给你一个字符串
s
,根据下述规则反转字符串:
- 所有非英文字母保留在原有位置。
- 所有英文字母(小写或大写)位置反转。
返回反转后的
s
。class Solution { public: bool isLetter(char ch) { if((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) return true; else return false; } // 注意isalpha()用于判断字符而不是字母 string reverseOnlyLetters(string s) { int begin = 0, end = s.size() - 1; while(begin < end) { // 注意:缺少判断条件 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; } };
2. 字符串最后一个单词的长度
字符串最后一个单词的长度_牛客题霸_牛客网 (nowcoder.com)
描述
计算字符串最后一个单词的长度,单词以空格隔开,字符串长度小于5000。(注:字符串末尾不以空格为结尾)
输入描述:
输入一行,代表要计算的字符串,非空,长度小于5000。
输出描述:
输出一个整数,表示输入字符串最后一个单词的长度。
#include <iostream> #include <string> using namespace std; int main() { string str = ""; // cin >> str; // cin 默认 空格&换行 终止读取 // 使用 getline() 控制读取的结束条件 getline(cin, str); size_t i = str.rfind(' '); if(i != string::npos) { cout << str.size() - (i+1) << endl; } else { cout << str.size() << endl; } return 0; } //我们也可以利用cin 默认 空格&换行 终止读取 写出更简约的代码 int main() { string str = ""; while(cin >> str) {} cout << str.size(); }
3. 字符串相加
415. 字符串相加 - 力扣(LeetCode)
给定两个字符串形式的非负整数
num1
和num2
,计算它们的和并同样以字符串形式返回。你不能使用任何內建的用于处理大整数的库(比如
BigInteger
), 也不能直接将输入的字符串转换为整数形式。class Solution { public: string addStrings(string num1, string num2) { string ans = ""; int end1 = num1.size()-1, end2 = num2.size()-1; int next = 0; // 进位 // 注意:while(end1 >= 0 && end2 >= 0) 无法处理连续进位的情况(999999+11) while(end1 >= 0 || end2 >= 0) { int x1 = end1 >= 0 ? num1[end1] - '0' : 0; int x2 = end2 >= 0 ? num2[end2] - '0' : 0; int ret = x1 + x2 + next; next = ret / 10; ret %= 10; ans += (ret + '0'); end1--, end2--; } if(next == 1) ans += '1'; reverse(ans.begin(), ans.end()); return ans; } }; // 效率:尾插 + 逆置 > 头插
4. 验证回文串
125. 验证回文串 - 力扣(LeetCode)
如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个 回文串 。
字母和数字都属于字母数字字符。
给你一个字符串
s
,如果它是 回文串 ,返回true
;否则,返回false
。class Solution { public: bool isPalindrome(string s) { string s1; auto it = s.begin(); while (it != s.end()) { if (*it >= 'A' && *it <= 'Z') s1 += (*it + 32); if (*it >= 'a' && *it <= 'z') s1 += *it; if(*it >= '0' && *it <= '9') s1 += *it; ++it; }cout << s1 << endl; string rs1; auto rit = s1.rbegin(); while (rit != s1.rend()) { rs1 += *rit; ++rit; }cout << rs1 << endl; if (s1 == rs1) return true; return false; } };
四、string类的模拟实现
注意:本文string类的模拟实现并不够专业标准,仅供初学者连续参考
#pragma once #include<iostream> #include<assert.h> using namespace std; namespace mystring { 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; } // 全缺省构造函数 string(const char* str = "") :_size(strlen(str)) , _capacity(_size) { _str = new char[_capacity + 1]; strcpy(_str, str); } /*string(const string& s) { _str = new char[s._capacity + 1]; strcpy(_str, s._str); _size = s._size; _capacity = s._capacity; }*/ void swap(string& s) { std::swap(_str, s._str); std::swap(_size, s._size); std::swap(_capacity, s._capacity); } // 拷贝构造 string(const string& s) :_str(nullptr) , _size(0) , _capacity(0) { string tmp(s._str); swap(tmp); } // operator= /*string& operator=(const string& s) { if (&s != this) { char* tmp = new char[s._capacity + 1]; strcpy(tmp, s._str); delete[] _str; _str = tmp; _size = s._size; _capacity = s._capacity; } return *this; }*/ /*string& operator=(const string& s) { if (this != &s) { string tmp(s); //this->swap(tmp); swap(tmp); } return *this; }*/ string& operator=(string tmp) { swap(tmp); return *this; } ~string() { delete[] _str; _str = nullptr; _size = _capacity = 0; } char& operator[](size_t pos) { assert(pos < _size); return _str[pos]; } const char& operator[](size_t pos) const { assert(pos < _size); return _str[pos]; } size_t capacity() const { return _capacity; } size_t size() const { return _size; } const char* c_str() const { return _str; } void clear() { _str[0] = '\0'; _size = 0; } bool empty()const { return _str == nullptr; } void reserve(size_t n) { if (n > _capacity) { char* tmp = new char[n + 1]; strcpy(tmp, _str); delete[] _str; _str = tmp; _capacity = n; } } void resize(size_t n, char ch = '\0') { if (n < _size) { _str[n] = '\0'; _size = n; } else { if (n > _capacity) { reserve(n); } for (size_t i = _size; i < n; i++) { _str[i] = ch; } _str[n] = '\0'; _size = n; } } void push_back(char ch) { if (_size == _capacity) { reserve(_capacity == 0 ? 4 : _capacity * 2); } _str[_size++] = ch; _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; } // insert(0, 'x') void insert(size_t pos, char ch) { assert(pos <= _size); if (_size == _capacity) { reserve(_capacity == 0 ? 4 : _capacity * 2); } // 类型提升会导致 -1 > 0 //size_t end = _size; //while (end >= pos) //{ // _str[end + 1] = _str[end]; // --end; //} /*//方案一 int end = _size; while (end >= (int)pos) { _str[end + 1] = _str[end]; --end; }*/ // 方案二 size_t end = _size + 1; while(end > pos) { _str[end] = _str[end - 1]; --end; } _str[pos] = ch; _size++; } void insert(size_t pos, const char* str) { assert(pos <= _size); size_t len = strlen(str); if (_size + len > _capacity) { reserve(_size + len); } int end = _size; while (end >= (int)pos) { _str[end + len] = _str[end]; --end; } strncpy(_str + pos, str, len); _size += len; } string& insert_s(size_t pos, char ch) { assert(pos <= _size); if (_size == _capacity) { reserve(_capacity == 0 ? 4 : _capacity * 2); } char* end = _str + _size; while (end >= _str + pos) { *(end + 1) = *end; --end; } _str[pos] = ch; _size++; return *this; } string& insert_s(size_t pos, const char* str) { assert(pos <= _size); size_t len = strlen(str); if (_size + len > _capacity) { reserve(_size + len); } char* end = _str + _size; while (end >= _str + pos) { *(end + len) = *end; --end; } strncpy(_str + pos, str, len); _size += len; return *this; } void erase(size_t pos, size_t len = npos) { assert(pos <= _size); if (len == npos || pos + len >= _size) { _str[pos] = '\0'; _size = pos; } else { size_t begin = pos + len; while (begin <= _size) { _str[pos] = _str[begin]; pos++, begin++; } _size -= len; } } string& erase_s(size_t pos, size_t len = npos) { assert(pos < _size); size_t left = _size - pos; if (len >= left) { _str[pos] = '\0'; _size = pos; } else { strcpy(_str + pos, _str + pos + len); _size -= len; } return *this; } bool operator<(const string& s) const { return strcmp(_str, s._str) < 0; } 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 !(*this == s); } // 返回字符ch在string中第一次出现的位置 size_t find(char ch, size_t pos = 0) { for (size_t i = pos; i < _size; i++) { if (_str[i] == ch) { return i; } } return npos; } // 返回子串s在string中第一次出现的位置 size_t find(const char* s, size_t pos = 0) { const char* ret = strstr(_str + pos, s); if (ret == nullptr) { return npos; } else { return ret - _str; } } string substr(size_t pos, size_t len = npos) { string s; size_t end = pos + len; if (len == npos || pos + len >= _size) { len = _size - pos; end = _size; } s.reserve(len); for (size_t i = pos; i < pos + len; i++) { s += _str[i]; } return s; } private: char* _str; size_t _size; size_t _capacity; //static size_t npos; public: const static size_t npos; //const static size_t npos = -1; // 特例(static 修饰的变量不能在此处初始化) }; // const static 修饰的整型变量除外 //size_t string::npos = -1; const size_t string::npos = -1; ostream& operator<<(ostream& out, const string& s) { for (auto ch : s) out << ch; return out; } istream& operator>>(istream& in, string& s) { s.clear(); char buff[129]; size_t i = 0; char ch; ch = in.get(); while (ch != ' ' && ch != '\n') { buff[i++] = ch; if (i == 128) { buff[i] = '\0'; s += buff; i = 0; } ch = in.get(); } if (i != 0) { buff[i] = '\0'; s += buff; } return in; } }