目录
0.前言
1.为什么学习string类
1.1 C语言字符串的局限性
1.2 C++ string类的优势
2.标准库中的string类
2.1 字符串作为字符序列的类
2.2 接口与标准容器类似
2.3 基于模板的设计
2.4 编码和字符处理
3.string类的常用接口说明
3.1构造函数
3.1.1默认构造函数
3.1.2拷贝构造函数
3.1.3子字符串构造函数
3.1.4从C字符串构造
3.1.5从字符序列构造
3.1.6填充构造函数
3.2 容量操作函数
3.2.1 size() 和 length()
3.2.2 max_size()
3.2.3 resize()
3.2.4 capacity()
3.2.5 reserve()
3.2.6 clear()
3.2.7 empty()
3.3访问及遍历操作
3.3.1 operator[]
3.3.2 迭代器
3.3.3 范围for
3.4类对象修改操作
3.4.1 operator+=
3.4.2 append
3.4.3 push_back
3.4.4 assign
3.4.5 insert
3.4.6 erase
3.4.7 replace
3.4.8 swap
3.4.9 pop_back
4.小结
(图像由AI生成)
0.前言
在现代软件开发中,文本处理是不可或缺的一部分。C++作为一种强大的编程语言,提供了多种方式来处理字符串。在本文中,我们将深入探讨C++标准库中的string
类,了解其设计理念、功能以及使用方式。
1.为什么学习string类
学习C++中的string
类对于任何使用C++进行软件开发的程序员来说都是至关重要的,尤其是从C语言的传统字符串处理转变而来的开发者。以下几点阐述了学习string
类的重要性:
1.1 C语言字符串的局限性
C语言中的字符串处理依赖于字符数组和一系列标准库函数(如strcpy
, strcat
, strlen
等)。这些方法虽然基本能满足需求,但存在几个显著的缺点:
-
安全性问题:C语言中的字符串操作常常涉及直接的内存操作,这容易造成缓冲区溢出、内存泄露等安全问题。例如,
strcpy
函数在复制数据时不会检查目标缓冲区的大小,这可能导致超出其容量的写操作。 -
效率低下:C语言的字符串操作通常需要手动管理字符串长度和内存,如需拼接字符串时,可能需要多次调用
strlen
来获取当前长度,然后再添加新内容,这样的操作效率较低。 -
使用不便:C语言的字符串需要程序员对内存非常小心地操作,容易出错且代码难以维护。
1.2 C++ string类的优势
与C语言的基本字符串处理相比,C++的string
类提供了一个更安全、更高效、更易用的字符串操作方式:
-
自动内存管理:
string
类自动管理内存,使用者不需要关心内存分配和释放的问题,极大地降低了内存泄漏和缓冲区溢出的风险。 -
丰富的成员函数:
string
类内置了大量的成员函数,如append
,find
,replace
,substr
等,使得字符串的处理更为方便和直观。 -
动态大小:
string
对象可以根据需要动态增长和缩小,无需预先声明最大容量,这一点与静态大小的C语言字符数组形成鲜明对比。 -
操作符重载和迭代器支持:
string
类重载了多个操作符(如+
、=
等),支持迭代器,使得字符串操作更符合C++的对象操作习惯,也支持现代C++中的范围for
循环和算法库函数。 -
兼容性和灵活性:尽管
string
以字节为单位进行操作,对于多字节字符集的支持可能有限,但它在大多数情况下能够兼容处理UTF-8等编码,尤其是在使用支持这些编码的库时。
2.标准库中的string类
C++的string
类是标准库中一个非常重要的组成部分,主要用于表示和操作字符序列。它被设计为易于使用且功能强大,以适应现代软件开发中对文本处理的复杂需求。以下是对string
类详细介绍的几个关键方面。如果想要进一步地了解string的详细特性与使用方法,可以参考官方文档,详见:string - C++ Reference (cplusplus.com)。
2.1 字符串作为字符序列的类
string
类本质上是一个用于表示字符序列的容器。它封装了字符数组的许多复杂操作,提供了一种更安全、更直观的方式来处理文本数据。用户不需要关心底层的字符数组和内存管理,所有这些都由string
类自动处理。
2.2 接口与标准容器类似
string
类的接口设计借鉴了标准STL容器的模式,例如vector
和deque
。这意味着string
提供了类似于这些容器的多种成员函数,如迭代器支持、元素访问、修改操作、容量查询等。这种设计使得string
类既熟悉又易于使用对于已经熟悉其他C++标准容器的开发者。
2.3 基于模板的设计
string
类实际上是basic_string
模板的一个特化实例,使用char
类型作为字符类型。basic_string
是一个模板类,可以用不同的字符类型来实例化,如wchar_t
、char16_t
、char32_t
等,对应的实例分别可以处理宽字符或Unicode字符。
basic_string<char>
被typedef为string
。basic_string<wchar_t>
被typedef为wstring
。
这种模板设计提供了极大的灵活性,允许string
类以几乎相同的方式处理不同类型的字符数据。
2.4 编码和字符处理
尽管string
是以单字节字符char
进行操作,这使得它在默认情况下适用于ASCII编码的字符串处理,但对于多字节字符集(如UTF-8)的支持则需要更多注意。由于string
按字节操作,对于包含多字节字符的字符串,如UTF-8编码的文本,长度和位置的计算可能与字符实际显示的数量不同。这意味着在处理多字节字符集时,开发者可能需要使用额外的库或函数来正确解析和处理这些字符。
3.string类的常用接口说明
3.1构造函数
default (1) | string(); |
---|---|
copy (2) | string (const string& str); |
substring (3) | string (const string& str, size_t pos, size_t len = npos); |
from c-string (4) | string (const char* s); |
from sequence (5) | string (const char* s, size_t n); |
fill (6) | string (size_t n, char c); |
string
类提供了多种构造函数,允许以不同的方式创建和初始化字符串对象。下面详细介绍这些构造函数的用法和适用场景,结合示例代码进行说明。
3.1.1默认构造函数
string();
默认构造函数创建一个空的字符串。这是最简单的构造函数,用于初始化时不提供任何字符内容的情况。
std::string str1; // 创建一个空字符串
3.1.2拷贝构造函数
string(const string& str);
拷贝构造函数用于创建一个新的字符串,其内容是另一个已存在的string
对象的副本。这个函数在复制字符串或函数返回字符串时经常使用。
std::string original = "Hello";
std::string copy(original); // 使用拷贝构造函数
3.1.3子字符串构造函数
string(const string& str, size_t pos, size_t len = npos);
这个构造函数从一个现有的string
对象创建一个新字符串,开始于指定的pos
位置,长度为len
。如果len
为npos
(默认值),则复制从pos
开始到原字符串结束的部分。
std::string text = "Hello world";
std::string sub(text, 6, 5); // 从位置6开始,长度为5,结果为"world"
3.1.4从C字符串构造
string(const char* s);
这个构造函数使用C风格的字符串(以null终止的字符数组)初始化string
对象。它复制字符直到遇到null字符。
const char* cstr = "C++ string";
std::string str2(cstr); // 使用C字符串构造,结果为"C++ string"
3.1.5从字符序列构造
string(const char* s, size_t n);
这个构造函数从指定的字符数组创建一个字符串,复制前n
个字符。这适用于从部分字符数组构建字符串的情况。
const char* data = "Example";
std::string part(data, 4); // 只取前4个字符,结果为"Exam"
3.1.6填充构造函数
string(size_t n, char c);
创建一个字符串,其中包含n
个重复的字符c
。这个构造函数在需要初始化固定长度和内容的字符串时非常有用。
std::string filled(5, 'a'); // 创建一个包含5个'a'的字符串,结果为"aaaaa"
3.2 容量操作函数
string
类提供了一组容量操作函数,允许开发者查询和修改字符串的存储特性。这些功能对于优化性能和管理内存非常重要。以下是这些函数的详细介绍和示例代码:
3.2.1 size() 和 length()
size_t size() const;
size_t length() const;
这两个函数都返回字符串当前的长度,即字符串中字符的数量。在 std::string
中,size()
和 length()
是等价的,提供了相同的功能。
std::string str = "Hello, world!";
std::cout << "Length of string: " << str.length() << std::endl; // 输出 13
std::cout << "Size of string: " << str.size() << std::endl; // 同样输出 13
3.2.2 max_size()
size_t max_size() const;
max_size()
函数返回字符串可能的最大长度,这是由于系统或库实现的限制决定的。这不是一个固定值,而是表示字符串可以达到的最大理论长度。
std::string str;
std::cout << "Maximum size of string: " << str.max_size() << std::endl; // 输出可能的最大长度
3.2.3 resize()
void resize(size_t n);
void resize(size_t n, char c);
resize()
函数更改字符串的长度。如果新的长度大于当前长度,新位置将用字符 c
填充(如果提供了 c
的话)。如果新的长度小于当前长度,多余的字符将被截断。
std::string str = "Hello";
str.resize(10, 'x'); // "Helloxxxxx"
str.resize(3); // "Hel"
3.2.4 capacity()
size_t capacity() const;
capacity()
函数返回为字符串分配的存储空间的大小,通常这个值大于或等于 size()
。这可以给出底层数组可能的空间大小,有助于了解内存使用情况。
std::string str = "Test";
std::cout << "Capacity of string: " << str.capacity() << std::endl; // 输出分配的内存大小
3.2.5 reserve()
void reserve(size_t n = 0);
reserve()
函数试图改变字符串的容量,即预先分配足够的内存来存储至少 n
个字符,避免多次增加字符串大小时的重复分配。
std::string str;
str.reserve(100); // 为存储至少100个字符预分配内存
std::cout << "New capacity after reserve: " << str.capacity() << std::endl;
3.2.6 clear()
void clear() noexcept;
clear()
函数删除字符串中的所有字符,使其长度变为0。这个函数不改变容量。
std::string str = "Something";
str.clear(); // 清空字符串
std::cout << "String after clear: '" << str << "'" << std::endl; // 输出 ''
3.2.7 empty()
bool empty() const;
empty()
函数检查字符串是否为空(即长度为0)。如果字符串为空,返回 true
,否则返回 false
。
std::string str;
std::cout << "Is the string empty? " << (str.empty() ? "Yes" : "No") << std::endl; // 输出 'Yes'
3.3访问及遍历操作
在 std::string
类中,访问和遍历字符串的方法包括使用下标操作符、迭代器和范围 for
循环。这些方法提供了灵活的方式来访问和遍历字符串中的字符。
3.3.1 operator[]
下标操作符 operator[]
允许你访问字符串中的特定位置的字符。这种访问方式是随机访问,时间复杂度为 O(1)。
std::string str = "Hello, world!";
char ch1 = str[0]; // 访问第一个字符,ch1 = 'H'
char ch2 = str[7]; // 访问第八个字符,ch2 = 'w'
std::cout << "First character: " << ch1 << std::endl;
std::cout << "Eighth character: " << ch2 << std::endl;
// 修改字符串中的字符
str[5] = '!';
std::cout << "Modified string: " << str << std::endl; // 输出 "Hello! world!"
3.3.2 迭代器
std::string str = "Hello";
std::cout << "Characters in string: ";
// 使用正向迭代器遍历字符串
for (auto it = str.begin(); it != str.end(); ++it) {
std::cout << *it << ' ';
}
std::cout << std::endl;
// 使用反向迭代器遍历字符串
std::cout << "Characters in reverse: ";
for (auto rit = str.rbegin(); rit != str.rend(); ++rit) {
std::cout << *rit << ' ';
}
std::cout << std::endl;
迭代器提供了一种方式来顺序访问容器中的每个元素。std::string
支持正向和反向迭代器(begin/end
和 rbegin/rend
)。
3.3.3 范围for
范围 for
循环是 C++11 引入的一个特性,它允许简洁地遍历容器中的所有元素。在使用范围 for
循环遍历 std::string
时,可以直接访问每个字符。
std::string str = "world";
std::cout << "Characters in string using range-for: ";
for (char c : str) {
std::cout << c << ' ';
}
std::cout << std::endl;
// 修改字符串中的字符(需要使用引用)
for (char &c : str) {
c = toupper(c); // 将字符转换为大写
}
std::cout << "Modified string: " << str << std::endl; // 输出 "WORLD"
3.4类对象修改操作
std::string
类提供了多种方法来修改字符串的内容,这些方法包括添加、删除、插入和替换字符等操作。下面是这些方法的详细介绍和使用示例:
3.4.1 operator+=
void operator+=(const string& str);
void operator+=(char c);
void operator+=(const char* s);
operator+=
用于将字符串、字符或C字符串附加到现有的 std::string
对象上。
std::string str = "Hello";
str += ", world!";
std::cout << str << std::endl; // 输出 "Hello, world!"
3.4.2 append
string& append(const string& str);
string& append(const string& str, size_t subpos, size_t sublen);
string& append(const char* s);
string& append(const char* s, size_t n);
string& append(size_t n, char c);
append
方法添加字符串、字符数组或多个相同字符到现有字符串的末尾。
std::string str = "Hello";
str.append(" world", 6); // 附加了 " world" 中的前6个字符
std::cout << str << std::endl; // 输出 "Hello world"
3.4.3 push_back
void push_back(char c);
push_back
将一个字符追加到字符串的末尾。
std::string str = "Hello";
str.push_back('!');
std::cout << str << std::endl; // 输出 "Hello!"
3.4.4 assign
string& assign(const string& str);
string& assign(const string& str, size_t subpos, size_t sublen);
string& assign(const char* s);
string& assign(const char* s, size_t n);
string& assign(size_t n, char c);
assign
方法用于将一个新的字符串内容赋给 std::string
对象,替换原有的内容。
std::string str;
str.assign("Hello world");
std::cout << str << std::endl; // 输出 "Hello world"
3.4.5 insert
string& insert(size_t pos, const string& str);
string& insert(size_t pos, const char* s);
insert
方法在指定位置插入字符串或字符。
std::string str = "Hello world";
str.insert(6, "beautiful ");
std::cout << str << std::endl; // 输出 "Hello beautiful world"
3.4.6 erase
string& erase(size_t pos = 0, size_t len = npos);
erase
方法从字符串中删除指定位置和长度的字符。
std::string str = "Hello beautiful world";
str.erase(5, 10); // 删除从位置5开始的10个字符
std::cout << str << std::endl; // 输出 "Hello world"
3.4.7 replace
string& replace(size_t pos, size_t len, const string& str);
string& replace(size_t pos, size_t len, const char* s);
replace
方法替换字符串中指定位置和长度的部分为新的内容。
std::string str = "Hello beautiful world";
str.replace(6, 9, "awesome"); // 替换从位置6开始的9个字符
std::cout << str << std::endl; // 输出 "Hello awesome world"
3.4.8 swap
void swap(string& str);
swap
方法交换两个字符串的内容。
std::string str1 = "Hello";
std::string str2 = "World";
str1.swap(str2);
std::cout << "str1: " << str1 << " str2: " << str2 << std::endl; // 输出 "str1: World str2: Hello"
3.4.9 pop_back
void pop_back();
pop_back
删除字符串的最后一个字符。
std::string str = "Hello!";
str.pop_back();
std::cout << str << std::endl; // 输出 "Hello"
4.小结
在本篇博客中,我们深入探讨了C++标准库中的string
类,从其构造函数、容量操作函数,到访问及遍历操作,以及类对象的修改操作。string
类作为C++中处理字符串的核心工具,提供了丰富的接口来高效、安全地管理和操作字符串。掌握这些功能不仅可以提高编程效率,还能帮助开发者编写更加健壮和可维护的代码,有效地处理现代软件开发中的文本数据挑战。