首页 > 其他分享 >string类(上)(解析各种成员函数)

string类(上)(解析各种成员函数)

时间:2024-09-22 19:52:32浏览次数:3  
标签:std 字符 include string 迭代 字符串 解析 函数

文章目录


1. 为什么要学习string类

学习string类在编程中是非常重要的,特别是对于使用C++、C#、Java、Python等语言的开发者来说。以下是几个关键原因:

  1. 简化字符串操作string类提供了一系列的方法(或成员函数)来执行字符串的各种操作,比如连接、比较、查找、替换、分割等。这些操作在底层语言(如C语言的字符数组)中通常需要手动编写大量代码来实现,而使用string类则可以大大简化这些操作,使代码更加简洁、易读。
  2. 提高代码安全性:在处理字符串时,很容易遇到缓冲区溢出、内存泄漏等安全问题。string类通常会自动管理内存,比如动态分配和释放字符串所需的内存空间,从而减少了因手动管理内存而引发的错误。此外,一些string类还提供了安全的方法来避免常见的安全问题,如使用strncpy替代strcpy等。
  3. 提高开发效率:由于string类提供了丰富的字符串操作接口,开发者可以专注于业务逻辑的实现,而无需花费大量时间在字符串处理的基础工作上。这不仅可以提高开发效率,还可以减少因字符串处理不当而引入的错误。
  4. 面向对象编程(OOP)的优势string类作为面向对象编程中的一个类,继承了面向对象编程的所有优势,如封装、继承和多态等。这使得string类更加灵活、可扩展,并且可以轻松地与其他类进行集成和交互。
  5. 性能优化:虽然直接使用字符数组或指针在某些情况下可能会获得更好的性能,但现代string类通常经过高度优化,可以在不牺牲太多性能的情况下提供丰富的功能和更好的安全性。此外,随着编译器和运行时环境的不断改进,string类的性能也在不断提高。

综上所述,学习string类对于提高编程技能、开发效率和软件质量都非常重要。无论是在学习新的编程语言时,还是在处理与字符串相关的实际项目时,都应该深入了解和掌握string类的使用方法和最佳实践。

2. 标准库中的string类

std::string类定义在<string>头文件中,它代表了一个可变长度的字符序列。std::string提供了丰富的成员函数来支持字符串的各种操作,包括字符串的构造、赋值、连接、比较、查找、替换、插入、删除、访问字符、获取字符串大小和容量等。

2.1 构造函数

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;  
}

2.2 成员函数

std::string类提供了大量的成员函数来支持字符串操作。

  • size() 和 length():返回字符串中字符的数量。
  • empty():检查字符串是否为空。
  • clear():删除字符串中的所有字符。
  • append()+=:在字符串末尾追加字符或字符串。
  • insert():在字符串的指定位置插入字符或字符串。
  • erase():删除字符串中的字符或子字符串。
  • replace():替换字符串中的字符或子字符串。
  • find()rfind()find_first_of()find_last_of() 等:在字符串中查找字符或子字符串。
  • substr():返回字符串的一个子串。
  • c_str():返回一个指向以null结尾的字符数组的指针,该数组是字符串的副本。
  • at()operator[]:访问字符串中的字符,但at()会进行范围检查。
  • reverse(): 将字符串中的字符顺序反转。
  • capacity(): 返回当前分配给字符串的内存量(以字符为单位)。
  • resize(): 改变字符串的大小。
  • reserve(): 请求改变字符串的容量,参数指定了新的容量最小值。
  • to_string(): 将int,double等类型转换成string类型。

以下是一个使用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,则意味着未找到匹配项。

2.3 与内存相关的成员函数

1. 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;  
}
2.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;  
}
3. 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() 返回的值可能大于你请求的值。

2.4 其他函数的简单示例

4. 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;
}
5. 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;
}
6. 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;
}

2.5 string迭代器

在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;  
}

【注意】:

  • 当然在这个地方有没有觉得比较麻烦呢,此时我们就可以想到之前讲过的C++11中的auto关键字,用auto去自动识别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循环并不直接提供迭代器的访问,因此如果你需要迭代器的特定功能(如随机访问或修改字符串的特定部分),你可能需要坚持使用传统的迭代器遍历方法。

2.7 示例:如何分割网页链接

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;
}

标签:std,字符,include,string,迭代,字符串,解析,函数
From: https://blog.csdn.net/suye050331/article/details/142370350

相关文章

  • 深度解析SAP业务技术云平台的优势
    在当今数字化快速发展的时代,企业对于高效、灵活且安全的业务解决方案的需求日益增长。SAP业务技术云平台应运而生,它为企业带来了诸多显著的优势。 一、集成性优势1.全面的业务功能集成   SAP业务技术云平台集成了众多关键的业务功能,如财务、客户关系管理、项目管理、人员......
  • C++中模板的初级使用函数模板(刚刚接触模板概念的小白也能明白)
    文章目录模板分类函数模板函数模板的原理函数模板基本语法——typename以及class简单的函数模板多类型模板参数class和typename的选择类模板模板分类模板的核心思想是让编译器在编译时生成适用于具体类型的代码,这个过程称为模板实例化。C++中的模板分为两种:......
  • 【C++基础知识——std::visit函数理解】
    1.std::visit函数的定义std::visit通常用于访问std::variant类型的对象。std::variant是C++17引入的一种类型安全的联合体,可以存储多个类型中的一个。visit函数允许你通过一个访问者(visitor)来访问std::variant中存储的值,而不需要显式地检查存储的类型。2.示例#......
  • 数据中台已过时?解析向数据飞轮过渡的必然之路
    在数字化转型的大潮中,企业经历了从数据中台到数据飞轮的进化。数据中台,一度被视为企业建设数据能力的基础,它主要承担数据整合、管理和存储的功能。然而,随着时间的推移,我们发现仅建立数据中台并不能充分激活数据的潜力,其核心问题在于数据中台过于侧重于数据的集中管理,而忽视了数据的......
  • 地统计常用公式与概念介绍:插值、平稳假设、变异函数、块金、克里格、线性无偏等
      本文对插值、平稳假设、变异函数、克里格等常用的地学计算概念加以介绍,并对相关公式进行推导。1引言  我们由地学计算的几个基本概念入手,对相关理论方面的内容加以一定了解。  需要注意的是,以下内容如果单独来看或许有些不好理解,但一旦将其与实际应用结合,便会豁然开朗......
  • 神经网络:激活函数选择
        结论直接看——激活函数的选择方式        神经网络主体分为输入层、隐藏层和输出层三个模块。一般情况下,输入层只负责对数据的输入,并不做任何的变换。故而,激活函数的选择只涉及隐藏层和输出层两个模块。     神经网络主体图激......
  • CSP-J 2024 入门组初赛第一轮初赛试题及答案解析
    CSP-J2024入门组初赛第一轮初赛试题及答案解析一、单项选择题(共15题,每题2分,共计30分:每题有且仅有一个正确选项)132位int类型的存储范围是()A-2147483647~+2147483647B-2147483647~+2147483648C-2147483648~+2147483647D-2147483648~+2147483648......
  • CSP-S 2024 提高组初赛第一轮初赛试题及答案解析
    完整试题,CSP-S-2024CSP-S2024提高组初赛第一轮初赛试题及答案解析一、单项选择题(共15题,每题2分,共计30分:每题有且仅有一个正确选项)1在Linux系统中,如果你想显示当前工作目录的路径,应该使用哪个命令?()ApwdBcdClsDecho答案A解析Apwd:这个命令是“print......
  • CSP-S 2024 提高组初赛第一轮初赛试题及答案解析
    CSP-S2024提高组初赛第一轮初赛试题及答案解析一、单项选择题(共15题,每题2分,共计30分:每题有且仅有一个正确选项)1在Linux系统中,如果你想显示当前工作目录的路径,应该使用哪个命令?()ApwdBcdClsDecho答案A解析Apwd:这个命令是“printworkingdirectory......
  • 高等数学 3.6 函数图像的描绘
    利用导数描绘函数图形的一般步骤如下:(1)确定函数\(y=f(x)\)的定义域及函数所具有的某些特性(如奇偶性、周期性等),并求出函数的一阶导数\(f^{'}(x)\)和二阶导数\(f^{''}(x)\);(2)求出一阶导数\(f^{'}(x)\)和二阶导数\(f^{''}(x)\)在函数定义域内的全部零点,并求出函数\(f(x)......