学习string
类在编程中是非常重要的,特别是对于使用C++、C#、Java、Python等语言的开发者来说。以下是几个关键原因:
string
类提供了一系列的方法(或成员函数)来执行字符串的各种操作,比如连接、比较、查找、替换、分割等。这些操作在底层语言(如C语言的字符数组)中通常需要手动编写大量代码来实现,而使用string
类则可以大大简化这些操作,使代码更加简洁、易读。string
类通常会自动管理内存,比如动态分配和释放字符串所需的内存空间,从而减少了因手动管理内存而引发的错误。此外,一些string
类还提供了安全的方法来避免常见的安全问题,如使用strncpy
替代strcpy
等。string
类提供了丰富的字符串操作接口,开发者可以专注于业务逻辑的实现,而无需花费大量时间在字符串处理的基础工作上。这不仅可以提高开发效率,还可以减少因字符串处理不当而引入的错误。string
类作为面向对象编程中的一个类,继承了面向对象编程的所有优势,如封装、继承和多态等。这使得string
类更加灵活、可扩展,并且可以轻松地与其他类进行集成和交互。string
类通常经过高度优化,可以在不牺牲太多性能的情况下提供丰富的功能和更好的安全性。此外,随着编译器和运行时环境的不断改进,string
类的性能也在不断提高。综上所述,学习string
类对于提高编程技能、开发效率和软件质量都非常重要。无论是在学习新的编程语言时,还是在处理与字符串相关的实际项目时,都应该深入了解和掌握string
类的使用方法和最佳实践。
std::string
类定义在<string>
头文件中,它代表了一个可变长度的字符序列。std::string
提供了丰富的成员函数来支持字符串的各种操作,包括字符串的构造、赋值、连接、比较、查找、替换、插入、删除、访问字符、获取字符串大小和容量等。
std::string
类提供了多个构造函数,允许以不同的方式初始化字符串对象。
#include <iostream>
#include <string>
int main() {
// 使用默认构造函数创建一个空字符串
std::string str1;
// 使用C风格字符串初始化
std::string str2 = "Hello, World!";
// 使用另一个string对象初始化
std::string str3(str2);
// 使用指定数量的字符初始化
std::string str4(5, 'a'); // 结果为 "aaaaa"
// 使用迭代器范围初始化(需要包含其他容器,如std::vector)
// std::string str5(vec.begin(), vec.end()); // 假设vec是一个char类型的vector
std::cout << str1 << std::endl;
std::cout << str2 << std::endl;
std::cout << str3 << std::endl;
std::cout << str4 << std::endl;
return 0;
}
std::string
类提供了大量的成员函数来支持字符串操作。
at()
会进行范围检查。以下是一个使用std::string
类成员函数的示例:
#include <iostream>
#include <string>
int main() {
std::string str = "Hello, World!";
// 获取字符串大小
std::cout << "Size: " << str.size() << std::endl;
// 检查字符串是否为空
if (!str.empty()) {
std::cout << "The string is not empty." << std::endl;
}
// 在字符串末尾追加一个字符
str.push_back('!');
std::cout << str << std::endl;
// 在字符串末尾追加文本
str.append(" Append this.");
std::cout << str << std::endl;
// 替换文本
str.replace(7, 6, "Fantastic "); // 从索引7开始,替换6个字符为"Fantastic "
std::cout << str << std::endl;
// 访问字符(注意:operator[]不会进行范围检查)
if (!str.empty()) {
std::cout << "First character: " << str[0] << std::endl;
}
// 使用c_str()获取C风格字符串
const char* cstr = str.c_str();
std::cout << "C-style string: " << cstr << std::endl;
// 反转字符串
reverse(str.begin(), str.end());
std::cout << str << std::endl;
// 将int,double等类型转换成string类型
string stri = to_string(1234);
string strd = to_string(11.22);
cout << stri << " ; " << strd << endl;
return 0;
}
【注意】:
pos != std::string::npos
通常用于检查某个查找操作是否成功找到了指定的子字符串或字符。如果 pos
(即查找函数返回的位置)不等于 std::string::npos
,那么意味着找到了匹配项,且 pos
是匹配项在字符串中的起始位置(基于0的索引)。如果 pos
等于 std::string::npos
,则意味着未找到匹配项。capacity()
capacity()
成员函数返回当前分配给字符串的内存量(以字符为单位),这个量通常大于或等于字符串的实际长度(size()
返回的值)。capacity()
提供了关于字符串内部存储的额外信息,这对于优化内存使用或理解字符串操作的性能影响可能是有用的。
#include <iostream>
#include <string>
int main() {
std::string str = "Hello";
std::cout << "Size: " << str.size() << ", Capacity: " << str.capacity() << std::endl;
// 字符串长度可能不变,但capacity可能会增加
str.reserve(100);
std::cout << "Size: " << str.size() << ", Capacity: " << str.capacity() << std::endl;
return 0;
}
resize()
在C++中,std::string
类的resize
成员函数用于改变字符串的大小。这可以通过两种方式实现:一是通过添加额外的字符(通常是空格或指定的填充字符)来增加到指定的大小,二是通过截断字符串以减小其大小。
void resize(size_t n);
void resize(size_t n, char c);
resize
接受一个参数n
,表示新的字符串大小(以字符为单位)。如果n
小于当前字符串的大小,则字符串将被截断到前n
个字符。如果n
大于当前字符串的大小,则会在字符串的末尾添加足够多的空格(默认字符),以使字符串的大小达到n
。resize
除了接受新的大小n
外,还接受一个额外的参数c
,表示用于填充额外空间的字符。如果n
大于当前字符串的大小,则会在字符串的末尾添加足够多的c
字符,以使字符串的大小达到n
。#include <iostream>
#include <string>
int main() {
std::string str = "Hello";
// 增加字符串大小,使用空格填充
str.resize(10);
std::cout << "\"" << str << "\"" << std::endl; // 输出: "Hello "
// 增加字符串大小,使用特定字符填充
str.resize(15, '*');
std::cout << "\"" << str << "\"" << std::endl; // 输出: "Hello*****"
// 减小字符串大小
str.resize(5);
std::cout << "\"" << str << "\"" << std::endl; // 输出: "Hello"
return 0;
}
reserve()
reserve()
成员函数请求改变字符串的容量。参数指定了新的容量最小值。如果当前容量小于请求的新容量,则可能会重新分配内部存储以容纳至少那么多的字符(尽管实际分配的容量可能更大)。如果当前容量已经足够大,则reserve()
可能什么也不做。
reserve()
并不改变字符串的内容或大小(size()
),只是预留了足够的空间以便将来可以高效地添加更多字符,而无需频繁地重新分配内存。
#include <iostream>
#include <string>
int main() {
std::string str = "Hello";
std::cout << "Initial capacity: " << str.capacity() << std::endl;
// 请求至少100个字符的容量
str.reserve(100);
std::cout << "After reserve(100): " << str.capacity() << std::endl;
// 添加更多字符,可能不需要重新分配内存
str += " This is a long string to test capacity.";
std::cout << "After appending: " << str.capacity() << std::endl;
return 0;
}
请注意,capacity()
返回的值是一个估计值,并且对于不同的编译器和标准库实现,这个值可能会有所不同。此外,即使你调用了 reserve()
,标准库也允许实现分配比请求更多的内存,因此 capacity()
返回的值可能大于你请求的值。
insert()
和assign()
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1("hello world");
s1.append("ssssss");
cout << s1 << endl;
// 覆盖原字符串(不常用)
s1.assign("111111111");
cout << s1 << endl;
// 中间插入(防止效率过低,尽量少使用)
s1.insert(0, "hello");
cout << s1 << endl;
// 从第五个位置开始插入"world"
s1.insert(5, "world");
cout << s1 << endl;
// 从起始位置开始插入10个'x'
s1.insert(0, 10, 'x');
cout << s1 << endl;
// 使用迭代器插入10个'y'
s1.insert(s1.begin() + 10, 10, 'y');
cout << s1 << endl;
return 0;
}
at()
和operator[]
#include <iostream>
#include <string>
using namespace std;
int main()
{
try {
string s1("hello world");
s1.at(0) = 'x';
cout << s1 << endl;
// s1[15]; // 暴力处理
s1.at(15); // 温和的处理
}
catch (const exception& e)
{
cout << e.what() << endl;
} // 报异常
return 0;
}
erase()
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1("hello world");
// 从第五个位置开始删除一个字符
s1.erase(5, 1);
cout << s1 << endl;
// 从第五个开始全部删除
s1.erase(5);
cout << s1 << endl;
return 0;
}
在C++中,std::string
类(以及许多其他容器类)提供了迭代器(iterator)来访问其元素。迭代器是一种抽象的数据类型,它提供了一种统一的方法来访问容器中的元素,而无需了解容器的内部表示。对于std::string
来说,迭代器允许你按顺序访问字符串中的每个字符。
std::string
的迭代器是随机访问迭代器,这意味着它们支持高效的随机访问(即,你可以直接跳转到字符串中的任何位置)。此外,它们还支持递增(++
)、递减(--
)、算术运算(如+
和-
)、比较(如==
、!=
、<
等)以及解引用(*
)操作。
【迭代器类型】
std::string
的迭代器类型通常是通过在std::string
类型上调用begin()
和end()
成员函数获得的。begin()
返回一个指向字符串第一个字符的迭代器,而end()
返回一个指向字符串末尾“之后”位置的迭代器(即,一个“尾后迭代器”,它实际上不指向任何有效的字符,而是用作循环或算法的结束条件)。
std::string::iterator
是用于修改字符串内容的迭代器类型(如果字符串是可变的)。std::string::const_iterator
是用于只读访问字符串内容的迭代器类型。【使用迭代器】
你可以使用迭代器来遍历std::string
中的字符,读取它们,甚至修改它们(如果你有一个非常量字符串的迭代器)。
#include <iostream>
#include <string>
int main() {
std::string str = "Hello, World!";
// 使用迭代器遍历字符串
for (std::string::iterator it = str.begin(); it != str.end(); ++it) {
std::cout << *it; // 解引用迭代器以获取字符
}
std::cout << std::endl;
// 使用反向迭代器遍历字符串
for (std::string::reverse_iterator rit = str.rbegin(); rit != str.rend(); ++rit){
std::cout << *rit; // 解引用迭代器以获取字符
}
std::cout << std::endl;
// 如果你想要修改字符串(并且它是可变的),可以这样做
for (std::string::iterator it = str.begin(); it != str.end(); ++it) {
*it = toupper(*it); // 将所有字符转换为大写
}
std::cout << str << std::endl; // 输出修改后的字符串
// 对于常量字符串,使用const_iterator
const std::string constStr = "Another string";
for (std::string::const_iterator it = constStr.begin(); it != constStr.end(); ++it) {
std::cout << *it; // 只能读取,不能修改
}
std::cout << std::endl;
return 0;
}
【注意】:
it
的类型,是不是方便许多了呢?【基于范围的for循环】
从C++11开始,你还可以使用基于范围的for循环来遍历std::string
(或任何其他容器),这使得代码更加简洁。
#include <iostream>
#include <string>
int main() {
std::string str = "Hello, Range-based loop!";
// 使用基于范围的for循环遍历字符串
for (char c : str) {
std::cout << c;
}
std::cout << std::endl;
// 注意:基于范围的for循环不能直接用于修改字符串,因为它不提供对迭代器的直接访问
// 但你可以通过其他方式(如std::transform, char&)来修改字符串的内容
return 0;
}
然而,请注意,基于范围的for循环并不直接提供迭代器的访问,因此如果你需要迭代器的特定功能(如随机访问或修改字符串的特定部分),你可能需要坚持使用传统的迭代器遍历方法。
int main()
{
string url = "http://legacy.cpluscplus.com/reference/string/string/";
// 协议 域名 资源名
size_t pos1 = url.find("://");
string protocol; // 协议
if (pos1 != string::npos) // 用来判断是否找到了指定的字符串
{
protocol = url.substr(0, pos1);
}
cout << protocol << endl;
string domain; // 域名
string uri; // 资源
size_t pos2 = url.find('/', pos1 + 3); // 不能从起始位置开始找,会找到://
if (pos2 != string::npos)
{
domain = url.substr(pos1 + 3, pos2 - (pos1 + 3));
uri = url.substr(pos2 + 1);
}
cout << domain << endl;
cout << uri << endl;
return 0;
}