【C++】STL简介 | string类的常用接口

news2024/11/17 16:24:57

目录

STL简介

学string类前的铺垫

概念

为什么要学string类

string类的底层(了解)

编码表的故事

string类的常用接口与应用

3个必掌握的构造

赋值

访问字符operator[]

初识迭代器(iterator)

反向迭代器

用范围for遍历

string类对象的修改操作

插字符push_back()

插字符串append()

超好用的operator+=

指定位置插字符insert()

string类对象的容量操作

只开空间的reserve()

开空间+初始化的resize()


STL简介

什么是STL?

全称为Standard Template Library(标准模板库),是C++标准库的重要组成部分。

它不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架。

通俗来说,STL就是将常见的数据结构(如顺序表,链表,栈,队列,二叉树,哈希等)以模板的形式进行封装,使用时,不用人为再

去写,可以直接调用。并且,一些常见的通用的算法也不用自己实现,可以直接从STL调用。

六大组件:

这个目前了解即可,后面会具体学到。

STL的众多组件中,我们从容器里的string类学起。

学string类前的铺垫

概念

String类是用于处理字符串的类。String类提供了一系列的方法,用于处理字符串,包括连接字符串、查找子字符串、获取字符串长度等。

为什么要学string类

处理字符串的方法,我们之前在C语言中也学到过。如字符串函数strlen、strcpy、strcat……,对于字符串的处理主要是求长度、追加、查找等,这些就是string要代替那些字符串函数完成的操作。

那已经有了字符串函数,为什么还要搞一个string类出来呢?

这时因为,字符串函数与字符串是分离开的,不太符合面向对象编程 的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。

所以说,我们需要更好用的string类。

string类的底层(了解)

实际上,编C++的大佬并没有直接写一个string类,而是写了一个basic_string模板类。

string类在底层上实际是:basic_string 模板类的别名。

typedef basic_string<char, char_traits, allocator>string;

string就是basic_string<char>。是basic_string的char类型 的实例化。

❓问题来了:为什么不直接实现sring类,而是写一个模板呢?

这与编码表有关,我们接着往下看。

编码表的故事

计算机最开始是从美国发源的。美国人的交流是建立在ABCD的字母组合上,而计算机只认得01串。

为了在计算机上交流,美国人发明了一套ASKII编码,统一规定了字母和常用符号用哪些二进制数来表示。

这样一来,如果我们写进了一个'A',那计算机中实际存储的是用01表示出的65:

int main()
{
    char a = 'A';
    return 0;
}

如果我们要存储"happy"这个单词的话,那计算机中实际存储的分别是h、a、p、p、y对应的ASKII码值。

如果存数字1234,那会开4个char字节,依次存入1、2、3、4的ASKII值:

补充:所以说,string类的123+string类的1,不等于124!而是1231:

但ASKII编码表并不是万能的。对于美国等来说,字母加上常用字符有一百来个,一个字节八个比特位(最多表示256位)完全够用了。

但对于文化博大精深的中国来说,汉字、繁体字的数量庞大,一个字节根本不够表示,这要怎么办呢?

中国科学家齐心协力,自定了一套编码表,名叫GB2312。这时用两个字节,16个比特位,来表示常用的汉字。后来,经过不断更新,又出台了GBK标准,能表示更多的汉字。

这里补充一点:在中国的编码表中,读音相同的字是编在一起的。所以,净网行动下,跟国粹读音相同的词都会被屏蔽。如果你发“卧槽”,结果是“* *”,如果改发“握草” “沃槽”,结果还是“ * *”。

但是问题又来了:不同国家的人用不同的编码标准,一个美国人给一个中国人发文件,因为编码表译出来的不同,打开就是乱码了,这要怎么办?

为了解决这个问题,科学家发明了Unicode(万国码)。它包含了世界上所有的符号,所有语言都可以互通,一个网页上也可以显示多国语言。

但问题又来了:像中国这样文字多的国家,要用两个字节去表示字符,那对于原本一个字节就够用的语言,现在也要跟着用两个字节。这对计算机空间会造成极大的浪费。

对此的解决方案是:UTF-8!

“UTF-8(8位元,Universal Character Set/Unicode Transformation Format)是针对Unicode的一种可变长度字符编码。它可以用来表示Unicode标准中的任何字符,而且其编码中的第一个字节仍与ASCII相容,使得原来处理ASCII字符的软件无须或只进行少部分修改后,便可继续使用。因此,它逐渐成为电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码。”

(源自:百度百科)

所以说,这些字符不一定只占一个字节,也可能占两个字节甚至更多。因此,不能简单地实现string,需要一个模板,即basic_string。

“basic_string是一个模板类,它是C++标准库中用于处理字符串的通用工具类。它提供了一种灵活且可扩展的方式来处理字符串,允许使用不同的字符类型(例如char、wchar_t(宽字节)等)和分配策略。"

string类的常用接口与应用

string的这些接口,不用全记。主要是多用,在运用中,常见的那几个就都掌握了。

3个必掌握的构造

下面三个标”重点“的, 要掌握

注:用string类要包头文件#include<string>

函数名称功能说明
string() (重点)构造空的string类对象,即空字符串
string(const char* s) (重点)将char类型的字符串转换成string类型的字符串
string(size_t n, char c)string类对象中包含n个字符c
string(const string& s) (重点)拷贝构造函数

演示:

#include<iostream>
#include<string>     
using namespace std;
int main()
{
    string s1;  //构造空的string类对象
​
    string s2("hello");  //用C-string来构造string类对象
​
    string s3(s2);   //拷贝构造
​
    cout << s1 << endl;
    cout << s2 << endl;
    cout << s3 << endl;
    return 0;
}

其他构造就不一一演示了,最重要的是得学会看文档。通过文档,我们能快速上手它的用法。

下面演示,通过看文档,掌握substring:

用起来:

#include<iostream>
#include<string>
using namespace std;
int main()
{
    string s1("hello");  
    string s2(s1, 0, 2);  //从s1的第0位开始,往后拷贝2位
    
    cout << s1 << endl;
    cout << s2 << endl;
    return 0;
}

读文档必掌握的npos

在读文档时,我们常能看到“npos"。啥意思?

npos是一个常数,表示size_t的最大值。容器里出现npos,表示不存在的位置。

npos的一些用法:

1.string::npos作为函数返回类型,表示找不到。

例:

if(pos==string::npos){     //表示pos不存在
    cout<<"cannot found";
}
​
if(pos!=string::npos){      //表示pos存在
    ……
}

赋值

有三种赋值方式:

#include<iostream>
#include<string>
using namespace std;
int main()
{
    string s1("hello"),s2; 
    s2 = s1;         //Way 1
    cout << s2 << endl;
​
    s2 = "happy";    //Way 2
    cout << s2 << endl;
​
    s2 = 'h';       //Way 3
    cout << s2 << endl;
​
    return 0;
}

访问字符operator[](必掌握)

string实现了operator[],可以访问字符串的字符。

1.修改

因为返回的是引用,所以我们可以对返回的该字符进行修改。这使字符串用起来就像数组一样:

#include<iostream>
#include<string>
using namespace std;
int main()
{
    string s1("hello"); 
    cout << s1[1] << endl;
​
    s1[1] = 'p';
    cout << s1 << endl;
    return 0;
}

2.遍历

也可以遍历数组,配合size()使用:

#include<iostream>
#include<string>
using namespace std;
int main()
{
    string s1("hello"); 
    for (int i = 0; i < s1.size(); i++) {
        cout << s1[i] << "  ";
    }
    return 0;
}

补充说明一点:其实有个跟size()功能差不多的函数:length(),也能求字符串长度。

但是更推荐用size(),因为它更通用。许多容器里不能用length(),但可以用size()。

3.检查越界

operator[]内部会检查越界,一旦越界就会报错。

示例:

#include<iostream>
#include<string>
using namespace std;
int main()
{
    string s1("hello"); 
    cout << s1[10] << endl;  //越界
    return 0;
}

4.(了解) 功能相同的at()

at()的功能跟operator()相同,也是访问字符。

at()是像函数一样去调用:

#include<iostream>
#include<string>
using namespace std;
int main()
{
	string s1("hello"); 
	for (int i = 0; i < s1.size(); i++) {
		cout << s1.at(i) << "  ";      //注意调用方式
	}
	return 0;
}

那at()和operator()的区别是什么呢?

两者检查越界的方式不一样。opreator()是断言,激进一点的处理方式,断言会把程序直接终止掉。at()是抛异常,比较温和的处理方式。一般用try catrch来捕获异常,程序还能继续跑。

初识迭代器(iterator)

迭代器是STL框架设计的六大组件之一,非常重要。现阶段先对迭代器做一个大致的了解。

什么是迭代器 (iterator)?

"迭代器是一种对象,它能够用来遍历标准模板库容器中的部分或全部元素,每个迭代器对象代表容器中的确定的地址。

迭代器修改了常规指针的接口,迭代器是一种概念上的抽象,那些行为上像迭代器的东西都可以叫做迭代器。迭代器有很多不同的能力,它可以把抽象容器和通用算法有机的统一起来。"

(源自百度百科)

现阶段我们对迭代器的理解是:像指针一样的类型,有可能就是指针,也可能不是指针,但用法像指针一样。(内部细节得后面我们自己实现了才知道)

那我们就先认为它是指针。

迭代器都是在类的内部定义的,属于这个类域。

如果要用的话,记得标明类域:

string::iterator

一个类里面有四种迭代器:

iterator
const_iterator

reverse_iterator
const_reverse_iterator

接下来,每种迭代器都用来遍历一下。

➡️用迭代器iterator去遍历:

#include<iostream>
#include<string>
using namespace std;
int main()
{
	string s1("hello"); 
	string::iterator it = s1.begin();
	while (it != s1.end()) {     //Q:这里用<可以吗?   A:于string可以,但是其他容器可能就不行了。!=是通用的写法
		cout << *it << " ";   //用法像指针
		it++;
	}
	return 0;
}

begin获取一个字符的迭代器 ,end获取最后一个字符后一个位置的迭代器:

➡️用const_iterator去遍历:

#include<iostream>
#include<string>
using namespace std;

void PrintString(const string& s) {
	string::const_iterator it = s.begin();  //形参被const修饰,此时不能再用iterator,不然是权限的放大
	while (it != s.end()) {
		cout << *it << " ";
		it++;
	}
	cout << endl;
}
int main()
{
	string s1("hello"); 
	PrintString(s1);
	return 0;
}

补充:

其实string不太用迭代器,因为operator[]更好用。

那用迭代器遍历的意义是什么呢?

因为iterator是所有容器通用的访问方式,并且用法类似。某些容器如map/list/set……只能用迭代器访问。

反向迭代器

(先了解一下,后面会手把手实现反向迭代器)

反向迭代器,顾名思义,可以实现反向遍历数组。

reverse_iterator   //反向迭代器

➡️使用reverse_iterator遍历:

#include<iostream>
#include<string>
using namespace std;
int main()
{
    string s1("hello"); 
    string::reverse_iterator rit = s1.rbegin();   //使用反向迭代器
    while (rit != s1.rend()) {
        cout << *rit << " ";
        rit++;
    }
    cout << endl;
    return 0;
}

右闭 (区间) 左开 (区间) :

➡️使用const_reverse_iterator遍历:

#include<iostream>
#include<string>
using namespace std;
​
void PrintString(const string& s) {
    string::const_reverse_iterator it = s.rbegin();   //注意得用const_reverse_iterator
    while (it != s.rend()) {
        cout << *it << " ";
        it++;
    }
    cout << endl;
}
int main()
{
    string s1("hello"); 
    PrintString(s1);
    return 0;
}

用范围for遍历

遍历字符串,除了上面讲到的两种方法,还可以用范围for去遍历:

#include<iostream>
#include<string>
using namespace std;
int main()
{
    string s1("hello"); 
    for (auto e : s1) {    //把s1的每个字符取出来,依次赋给e
        cout << e << " ";
    }
    cout << endl;
    return 0;
}

范围for遍历的好处在于:既能自动迭代,又能自动判断结束。

不过,范围for在底层实现上没啥技术含量,其实就是迭代器。

string类对象的修改操作

插字符push_back()

示例:

#include<iostream>
#include<string>
using namespace std;
int main()
{
    string s1("hello ");
    s1.push_back('u');    //只能插入一个字符,不能插字符串!
    cout << s1 << endl;
    return 0;
}

插字符串append()

这里用得最多的是(1)和(3):

示例:

#include<iostream>
#include<string>
using namespace std;
int main()
{
    string s1("hello ");
    s1.append("world");   //只能插字符串,不能插字符
    cout << s1 << endl;
    return 0;
}

超好用的operator+=(必掌握)

刚刚那两位,在operator+=面前,就相形见绌了。operator+=不仅能加字符、字符串,甚至还能加对象!

示例:

#include<iostream>
#include<string>
using namespace std;
int main()
{
    string s1("hello ");  
    s1 += "world";   //插字符串
    s1 += '!';       //插字符
​
    string s2("see u soon");
    s1 += s2;         //插对象
    cout << s1 << endl;
    return 0;
}

指定位置插字符insert()

要掌握(1)、(3)、(6):

练习:

力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台(字符串相加)

这里提一嘴,insert、erase(删除)、replace这样的尽量少用,效率堪忧。

string类对象的字符串操作

把string返回成char*的c_str

某些情况下,字符串需要兼容c的接口,这时就需要用c_str():

 

注意看,它把string类的字符串用char*的形式返回回来了。也就是说,返回一个指向数组的指针。

大部分情况下,string类型的字符串和char类型的字符串用着没差别。

但下面这种是例外,用此例展示string和char的区别:

#include<iostream>
#include<string>
using namespace std;
int main()
{
    string s1("hello ");
    cout << s1 << endl;      //调用重载的流插入运算符打印的
    cout << s1.c_str() << endl;     //调用字符串打印的
    cout << endl;
​
    s1 += '\0';
    s1 += "world";
    cout << s1 << endl;          //string打印结束以size为准,不受'\0'的影响
    cout << s1.c_str() << endl;   //遇'\0'则停
​
    return 0;
}

应用场景:

我们学过C语言中的fopen,他的左操作数就得为char*类型

FILE * fopen ( const char * filename, const char * mode );

那要用到fopen的场景下,string类就不能直接作为左操作数,而是要先转化成char*类型。

int main()
{
    string filename("text.txt");
    // FILE*pf=fopen(filename, "r");   //×
    FILE* pf = fopen(filename.c_str(), "w");  //√
    return 0;
}

查找位置的find()

find:从pos位置开始,往后查找字符s,找到了返回其下标。如果不指定pos,默认从下标0开始找。

size_t find (const string& str, size_t pos = 0) const;
size_t find (const char* s, size_t pos = 0) const;
size_t find (const char* s, size_t pos, size_t n) const;
size_t find (char c, size_t pos = 0) const;

示例:

int main()
{
    string s1("hello");
    cout << s1.find('e',0) << endl;   
    return 0;
}

镜像接口rfind:查找指定字符最后一次出现的位置,返回下标。

截取子字符串的substr()

substr: 从 pos 位置开始,截取 n 个字符 拷贝为子字符串,然后将子字符串返回。

string substr (size_t pos = 0, size_t len = npos) const;

示例:

int main()
{
    string s1("hello");
    cout << s1.substr(1,2) << endl;  
    return 0;
}

string类对象的容量操作

只开空间的reserve()

注意:是reserve(保留)不是reverse(逆置)。

void reserve (size_t n = 0);

当n大于当前字符串容量时,会扩容至n;

当小于,则容量不变。

此函数对字符串长度没有影响,也不能更改其内容。

注意看capacity的变化:

开空间+初始化的resize()

相比reserve的之开空间,resize既能开空间,同时还能初始化。

void resize (size_t n);     //⑴不指定初始化的值,那默认初始化为
void resize (size_t n, char c);    //⑵指定初始化为c

示例⑴:

示例⑵:

string与其他类型之间的转换

string类中提供了类型转换的功能,十分好用:

 

stoi表示string to int,其他名称同理。

示例:

int main()
{
    int val = 99;
    string str=to_string(val);  //将int类型的99转成string类型
​
    str = "999";
    val = stoi(str);        //将string类型的999转成int类型
                          //注:转变的是内容的类型,即99在string和int之间转换。而非val、str的类型
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1032819.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

uniapp获取一周日期和星期

UniApp可以使用JavaScript中的Date对象来获取当前日期和星期几。以下是一个示例代码&#xff0c;可以获取当前日期和星期几&#xff0c;并输出在一周内的每天早上和晚上&#xff1a; // 获取当前日期和星期 let date new Date(); let weekdays ["Sunday", "M…

剔除数据中的异常值(python实现)

目录 一、3σ原则 二、箱线图发现异常值 三、boxcox数据变换 一、3σ原则 该准则仅局限于对正态或近似正态分布的样本数据处理,此外,当测量次数少的情形用准则剔除粗大误差是不够可靠的。 异常值是指样本中的个别值,其数值明显偏离其余的观测值。异常值也称离群点,异常…

xpath定位不包含某种属性的元素

今天定位一个页面中的input文本框&#xff0c;发现竟然有两个几乎一模一样的html代码的input文本框。 唯一不同的是&#xff0c;一图中的input有一个comps"[object Object],[object Object]"的属性和属性值&#xff0c;二图则没有。我要定位的是二图中的input&#x…

AUTOSAR实战篇:手把手带你搞定Watchdog协议栈

AUTOSAR实战篇:手把手带你搞定Watchdog协议栈 前言 小T出品,必是精品! 手把手搞定Watchdog协议栈,你值得拥有! 正文 在进行Watchdog协议栈实战之前,建议先阅读小T之前有关Watchdog协议栈的两篇文章《Watchdog协议栈上》与《Watchdog协议栈下》先了解下在AUTOSAR框架下的W…

【Redis】第7讲 常用的操作命令

连接redis并测试 redis-cliping 设置并获取数据 set k1 chinaget k1 测试性能 rootsue-virtual-machine:~# redis-benchmark ctrlc 退出redis,执行命令5秒就ctrlc关闭&#xff0c;性能要看电脑的配置高低 默认16个数据库 vim /opt/redis-5.0.4/redis.conf 数据库键的数量 …

深入理解web安全攻防策略,看完这一篇就够了

前言 互联网时代&#xff0c;数据安全与个人隐私信息等受到极大的威胁和挑战&#xff0c;本文将以几种常见的攻击以及防御方法展开分析。 1. XSS (跨站脚本攻击) 定义&#xff1a;通过存在安全漏洞的Web网站注册用户的浏览器内运行非法的HTML标签或JavaScript进行的一种攻击…

王道考研数据结构代码总结(第八章)

目录 排序插入排序直接插入排序折半插入希尔排序链表的插入排序插入类排序总结 交换排序冒泡排序快速排序 选择排序简单选择排序堆排序堆的插入堆的删除 归并排序基数排序 本文包含王道考研讲课中所涉及的数据结构中的所有代码&#xff0c;当PPT代码和书上代码有所区别时以咸鱼…

“智慧”北京,人工智能引领“新风尚”

原创 | 文 BFT机器人 北京时间&#xff0c;9月15日&#xff0c;北京人工智能产业峰会暨中关村科学城科创大赛颁奖典礼在北京中关村举行&#xff0c;同时惠阳还举行了“中关村人工智能大模型产业集聚区”启动建设的揭牌仪式。 此次大会围绕北京AI产业的建设与发展&#xff0c;各…

一篇文章彻底弄懂单调栈!!!

前言 最近梳理完中间件后荔枝一边学项目一边刷算法&#xff0c;一刷了代码随想录中的字符串、双指针、栈和队列以及单调栈。其中感觉比较有难度的还是单调栈嘿&#xff0c;因此有必要(水)梳理一篇文章来复盘一下单调栈的相关知识~ 希望复盘完后可以有所收获&#xff01; 文章目…

某音网页端 X-Bogus 参数

逆向目标 目标&#xff1a;某音网页端用户信息接口 X-Bogus 参数 接口&#xff1a;aHR0cHM6Ly93d3cuZG91eWluLmNvbS9hd2VtZS92MS93ZWIvdXNlci9wcm9maWxlL290aGVyLw 什么是 JSVMP&#xff1f; JSVMP 全称 Virtual Machine based code Protection for JavaScript&#xff0c;即 …

根据条件关闭软件

使用下载工具时&#xff0c;经常出现磁盘空间已满&#xff0c;无法下载的情况。 使用shell写一个监控&#xff0c;每2分钟执行一次。判断当前磁盘的空间&#xff0c;低于2G时&#xff0c;关闭下载软件。 获取空间大小 ➜ ~ df -h …

如何运用API接口获取淘宝1688京东商品数据:从入门到实践

一、引言 随着电子商务的飞速发展&#xff0c;许多电商平台提供了API接口&#xff0c;允许开发者获取商品数据&#xff0c;以创建各种创新的应用。本文将详细介绍如何使用API接口获取商品数据&#xff0c;并通过代码示例进行演示。 二、API接口概述 1.API接口定义 API&…

【校招VIP】数据库理论之数据库范式

考点介绍&#xff1a; 范式是指关系数据库中的一种数据结构设计规范&#xff0c;用于规范关系型数据库中数据的存储方式&#xff0c;目的是为了消除冗余数据&#xff0c;减少数据的重复性&#xff0c;提高数据的一致性、完整性和正确性&#xff0c;避免数据的不一致性和冲突 …

三相组合式过电压保护器试验

三相组合式过电压保护器试验 试验目的 三相组合式过电压保护器主要分为有带串联间隙过压保护器和无间隙过压保护器两大类&#xff0c;其试验项目内容要求分别使用高压工频交流和高压直流电源。 三相组合式过电压保护器试验&#xff0c;主要是为了及早发现设备内部绝缘受潮及…

华为云云耀云服务器L实例评测|云耀云服务器L实例部署Dashdot服务器仪表盘

华为云云耀云服务器L实例评测&#xff5c;云耀云服务器L实例部署Dashdot服务器仪表盘 一、云耀云服务器L实例介绍二、Dashdot介绍2.1 Dashdot简介2.2 开发环境要求2.3 Yarn介绍 三、本次实践介绍3.1 本次实践简介3.2 本次环境规划 四、检查服务器环境4.1 购买云耀云服务器L实例…

Android中的缓存策略:LruCache和DiskLruCache

Android中的缓存策略&#xff1a;LruCache和DiskLruCache 导言 本篇文章主要是介绍Android中内置的两个缓存类的原理。所谓缓存&#xff0c;就是将获取的数据保存下来以便下次继续使用&#xff0c;这种技术尤其在网络请求和图片加载中有用&#xff0c;可以显著地提升App的性能…

SSM - Springboot - MyBatis-Plus 全栈体系(十三)

第三章 MyBatis 一、MyBatis 简介 1. 简介 MyBatis 最初是 Apache 的一个开源项目 iBatis, 2010 年 6 月这个项目由 Apache Software Foundation 迁移到了 Google Code。随着开发团队转投 Google Code 旗下&#xff0c; iBatis3.x 正式更名为 MyBatis。代码于 2013 年 11 月迁…

大模型训练显存优化推理加速方案

当前的深度学习框架大都采用的都是fp32来进行权重参数的存储&#xff0c;比如Python float的类型为双精度浮点数fp64&#xff0c;pytorch Tensor的默认类型为单精度浮点数fp32。随着模型越来越大&#xff0c;加速训练模型的需求就产生了。在深度学习模型中使用fp32主要存在几个…

R语言贝叶斯MCMC:GLM逻辑回归、Rstan线性回归、Metropolis Hastings与Gibbs采样算法实例...

原文链接&#xff1a;http://tecdat.cn/?p23236 在频率学派中&#xff0c;观察样本是随机的&#xff0c;而参数是固定的、未知的数量&#xff08;点击文末“阅读原文”获取完整代码数据&#xff09;。 相关视频 什么是频率学派&#xff1f; 概率被解释为一个随机过程的许多观测…

Spark SQL【电商购买数据分析】

Spark 数据分析 &#xff08;Scala&#xff09; import org.apache.spark.rdd.RDD import org.apache.spark.sql.{DataFrame, SparkSession} import org.apache.spark.{SparkConf, SparkContext}import java.io.{File, PrintWriter}object Taobao {case class Info(userId: Lo…