目录
一、为什么学习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. 仅仅反转字母
给你一个字符串
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. 字符串相加
给定两个字符串形式的非负整数
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. 验证回文串
如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个 回文串 。
字母和数字都属于字母数字字符。
给你一个字符串
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; } }