文章目录
- 1. 前言
- 2. 介绍
- 3. string类的使用
-
- 3.1 string类的构造函数
- 3.2 string类对象的容量操作
- 3.3 string类对象的访问及遍历操作
- 3.4 string类对象的修改操作
- 3.5 string类对象的字符串操作
- 3.6 string类的非成员函数
- 4. string类的模拟实现
1. 前言
C语言中,字符串是以
' ' 结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str 系列的库函数,但是这些库函数与字符串是分离开的,不太符合面向对象编程的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。所以C++引入了string 类作为标准库中用于处理字符串的数据类型。string 类封装了字符串的操作,提供了更方便、安全和高级的字符串处理功能,更符合面向对象编程的思想。
2. 介绍
英文解释:
简单来说:
- string是表示字符串的字符串类
- 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。
- string在底层实际是:basic_string模板类的别名,
typedef basic_string<char, char_traits, allocator>string; - 不能操作多字节或者变长字符的序列。
3. string类的使用
3.1 string类的构造函数
(constructor)函数代码 | 功能说明 |
---|---|
(默认构造函数)构造一个空字符串,长度为零个字符。 | |
(拷贝构造函数)构造一个 str 的副本。 | |
(子串构造函数)拷贝 str 从字符位置 pos 开始,跨越 len 个字符(如果 str 太短或者 len 为 |
|
拷贝由 s 指向的以空字符结尾的字符序列(C 字符串)。 | |
从由 s 指向的字符数组中拷贝前 n 个字符。 | |
使用字符 c 连续填充字符串 n 次。 | |
拷贝迭代器区间 |
实例:
// string constructor #include <iostream> #include <string> int main() { std::string s0("Initial string"); // 按照上述描述的顺序使用的构造函数 std::string s1; std::string s2(s0); std::string s3(s0, 8, 3); std::string s4("A character sequence"); std::string s5("Another character sequence", 12); std::string s6a(10, 'x'); std::string s6b(10, 42); // 42 is the ASCII code for '*' std::string s7(s0.begin(), s0.begin() + 7); std::cout << "s1: " << s1 << " s2: " << s2 << " s3: " << s3; std::cout << " s4: " << s4 << " s5: " << s5 << " s6a: " << s6a; std::cout << " s6b: " << s6b << " s7: " << s7 << ' '; return 0; }
输出结果:
3.2 string类对象的容量操作
函数名称 | 代码 | 功能说明 |
---|---|---|
size | 返回字符串有效字符长度。 | |
length | 返回字符串有效字符长度。 | |
max_size | 返回字符串可以达到的最大长度。 | |
resize | 将字符串调整为长度为 n 的字符。如果 n 小于当前字符串的长度,当前值将缩短为其前 n 个字符,删除第 n 个字符之后的字符。如果 n 大于当前字符串的长度,当前内容将被扩展,插入足够数量的字符以达到大小为 n。如果指定了字符 c,则新元素将被初始化为 c 的副本,否则它们将是值初始化的字符(空字符)。 | |
capacity | 以字节为单位,返回当前为字符串分配的存储空间的大小。 | |
reserve | 请求将字符串的容量调整为计划更改的大小,最多达到长度为 n 的字符。如果 n 大于当前字符串的容量,该函数将导致容器增加其容量以容纳 n 个字符(或更多)。在所有其他情况下,它被视为非强制性请求来缩小字符串的容量:容器的实现可以自由优化,并保留容量大于 n 的字符串。该函数不会对字符串长度产生影响,也不能修改其内容。 | |
clear | 清空有效字符。 | |
empty | 返回字符串是否为空(即长度是否为 0)。 |
3.3 string类对象的访问及遍历操作
函数名称 | 代码 | 功能说明 |
---|---|---|
operator[] | 返回字符串中位置 pos 处字符的引用。 | |
at | 返回字符串中位置 pos 处字符的引用。该函数会自动检查 pos 是否是字符串中一个有效的字符位置(即,pos 是否小于字符串的长度),如果不是,则抛出 out_of_range 异常。 | |
back | 返回字符串的最后一个字符的引用。该函数不应在空字符串上调用。 | |
front | 返回字符串的第一个字符的引用。 |
遍历操作
#include <iostream> #include <string> int main() { std::string str("Test string"); // 1.普通下标遍历 for (size_t i = 0; i < str.size(); ++i) std::cout << str[i]; std::cout << ' '; // 2.迭代器遍历 for (std::string::iterator it = str.begin(); it != str.end(); ++it) std::cout << *it; std::cout << ' '; // 3.范围for遍历 for (auto e : str) std::cout << e; std::cout << ' '; return 0; }
解释
- 普通下标遍历:
在这个部分的代码中,使用了一个普通的for 循环和下标操作符[] 来遍历字符串中的字符。循环变量i 从0开始递增,直到达到字符串的长度(通过str.size() 获取)。在每次迭代中,使用str[i] 来获取对应位置的字符,并使用std::cout 打印出来。- 迭代器遍历:
这部分代码使用了迭代器来遍历字符串中的字符。通过调用str.begin() 获取字符串的起始位置迭代器,并使用str.end() 获取字符串的结束位置迭代器。然后,使用一个for 循环和迭代器操作来遍历字符串中的每个字符。在每次迭代中,使用解引用操作符* 来获取当前迭代器指向的字符,并使用std::cout 打印出来。- 范围
for 遍历:
在这部分代码中,使用了范围for 循环来遍历字符串中的字符。通过for (auto e : str) 的语法,将字符串中的每个字符依次赋值给变量e 。在每次迭代中,直接使用变量e 来表示当前字符,并使用std::cout 打印出来。
3.4 string类对象的修改操作
函数名称 | 代码 | 功能说明 |
---|---|---|
operator+= | 在字符串末尾追加字符串 str、C字符串 s 或字符 c。 | |
append | 在字符串后追加一个字符串 str。 | |
push_back | 在字符串后尾插字符 c。 | |
insert | 在指定位置 pos 插入另一个字符串 str。 | |
erase | 从字符串的位置 pos 开始删除长度为 len 的字符(如果内容太短或者 len 为 |
3.5 string类对象的字符串操作
函数名称 | 代码 | 功能说明 |
---|---|---|
c_str | 回一个指向以空字符结尾的字符序列(即 C 字符串)的指针,该字符序列表示当前字符串对象的值。 | |
substr | 返回一个新构造的字符串对象,其值为当前对象的子串副本。子串是从字符位置 |
|
find | 在当前字符串的 pos 索引位置(默认为0)开始,查找子串 str(或C 字符串 s),返回找到的位置索引,-1表示查找不到子串。 | |
rfind | 在当前字符串的 pos 索引位置开始,反向查找子串 str(或C 字符串 s),返回找到的位置索引,-1表示查找不到子串。 | |
find_first_of | 在当前字符串的 pos 索引位置(默认为0)开始,在字符串中搜索与其参数中指定的任意字符匹配的第一个字符。 | |
find_last_of | 在当前字符串的 pos 索引位置(默认为0)开始,在字符串中搜索与其参数中指定的任意字符匹配的最后一个字符。 | |
find_first_not_of | 在当前字符串的 pos 索引位置开始,在字符串中搜索与其参数中指定的任意字符不匹配的第一个字符。 | |
find_last_not_of | 在当前字符串的 pos 索引位置开始,在字符串中搜索与其参数中指定的任意字符不匹配的最后一个字符。 |
3.6 string类的非成员函数
函数名称 | 代码 | 功能说明 |
---|---|---|
operator+ | 返回一个新构造的字符串对象,其值是将 lhs 的字符后跟 rhs 的字符连接而成。 | |
operator>> | 支持 cin 读入 string 类对象,遇到空格回车结束。 | |
operator<< | 支持 cout 输出 string 类对象。 | |
getline | 从输入流 ’。 |
|
relational operators | operator== operator!= operator< operator<= operator> operator>= |
支持比较操作符(==,!=,<,<=,>,>=) |
说明
getline(cin, s) 会获取前一个输入的换行符,需要在前面添加读取换行符的语句。如:getchar() 或cin.get() 。例如:
int n; string s; cin >> n; getchar(); //cin.get() getline(cin, s); // 可正确读入下一行的输入
operator+ 尽量少用,因为传值返回,导致深拷贝效率低。
4. string类的模拟实现
// string.h 文件 #pragma once #include <iostream> #include <string.h> #include <assert.h> using namespace std; namespace my_string { class string { friend ostream& operator<<(ostream& _cout, const my_string::string& s); friend istream& operator>>(istream& _cin, my_string::string& s); public: typedef char* iterator; typedef const char* const_iterator; public: string(const char* str = "") :_size(strlen(str)) , _capacity(_size) { _str = new char[_capacity + 1]; strcpy(_str, str); } void swap(string& s) { std::swap(_str, s._str); std::swap(_size, s._size); std::swap(_capacity, s._capacity); } string(const string& s) :_size(0) , _capacity(0) , _str(nullptr) { string tmp(s._str); swap(tmp); } string& operator=(const string& s) { if (this != &s) { string tmp(s); swap(tmp); } return *this; } ~string() { delete[] _str; _str = nullptr; _size = _capacity = 0; } iterator begin() { return _str; } iterator end() { return _str + _size; } const_iterator begin() const { return _str; } const_iterator end() const { return _str + _size; } size_t size()const { return _size; } size_t capacity()const { return _capacity; } bool empty()const { return _size; } 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 c) { if (n <= _size) { _str[n] = ' '; _size = n; } else { reserve(n); while (_size < n) { _str[_size] = c; ++_size; } _str[_size] = ' '; } } void push_back(char c) { if (_size == _capacity) { reserve(_capacity == 0 ? 4 : 2 * _capacity); } _str[_size++] = c; _str[_size] = ' '; } string& operator+=(char c) { push_back(c); return *this; } void append(const char* str) { size_t len = strlen(str); if (len + _size > _capacity) { reserve(len + _size); } strcpy(_str + _size, str); _size += len; } string& operator+=(const char* str) { append(str); return *this; } string substr(size_t pos, size_t len) { string s; size_t end = pos + len - 1; if (len == npos || pos + len >= _size) { len = _size - pos; end = _size; } s.reserve(len); for (size_t i = pos; i <= end; i++) { s += _str[i]; } return s; } void clear() { _str[0] = ' '; _size = 0; } const char* c_str()const { return _str; } char& operator[](size_t index) { assert(index < _size); return _str[index]; } const char& operator[](size_t index)const { assert(index < _size); return _str[index]; } bool operator<(const string& s) { return strcmp(_str, s._str) < 0; } bool operator==(const string& s) { return strcmp(_str, s._str) == 0; } bool operator<=(const string& s) { return *this < s || *this == s; } bool operator>(const string& s) { return !(*this <= s); } bool operator>=(const string& s) { return !(*this < s); } bool operator!=(const string& s) { return !(*this == s); } size_t find(char c, size_t pos) const { while (pos < _size) { if (_str[pos] == c) { return pos; } pos++; } return npos; } size_t find(const char* s, size_t pos) const { const char* p = strstr(_str + pos, s); if (p) return p - _str; else return npos; } string& insert(size_t pos, char c) { assert(pos <= _size); if (_size == _capacity) { reserve(_capacity == 0 ? 4 : 2 * _capacity); } size_t end = _size + 1; while (end >= pos + 1) { _str[end] = _str[end - 1]; --end; } _str[pos] = c; _size++; return *this; } string& insert(size_t pos, const char* str) { assert(pos <= _size); size_t len = strlen(str); if (len == 0) return *this; if (len + _size > _capacity) { reserve(len + _size); } size_t end = _size + len; while (end >= pos + len) { _str[end] = _str[end - len]; --end; } strncpy(_str + pos, str, len); _size += len; return *this; } string& erase(size_t pos, size_t len) { assert(pos < _size); if (len == npos || pos + len >= _size) { _str[pos] = ' '; _size = pos; } else { size_t begin = pos + len; while (begin <= _size) { _str[begin - len] = _str[begin]; ++begin; } _size -= len; } return *this; } private: char* _str; size_t _size; size_t _capacity; public: const static size_t npos; }; ostream& operator<<(ostream& _cout, const my_string::string& s) { for (auto e : s) _cout << e; return _cout; } istream& operator>>(istream& _cin, my_string::string& s) { s.clear(); char buff[129]; size_t i = 0; char ch; ch = _cin.get(); while (ch != ' ' && ch != ' ') { buff[i++] = ch; if (i == 128) { buff[i] = ' '; s += buff; i = 0; } ch = _cin.get(); } if (i != 0) { buff[i] = ' '; s += buff; } return _cin; } }; const size_t my_string::string::npos = -1;