C++中的string类操作详解
引言
针对C++中的string,本文主要讲解如何对其进行插入、删除、查找、比较、截断、分割以及与数字之间的相互转换等。
字符串插入
1. append方法
std::string str = "hello";
str.append(7, 'w'); // 在末尾添加7个字符'w'
str.append("wwwwwww"); // 在末尾添加一个字符串
- 两个参数:第一个是字符个数,第二个是目标字符
- 一个参数:要拼接的字符串
- 返回值是对象的引用,也就是可以有如下用法:
std::string str;
str.append(1, 'c').append("abc").append('d'); // str="cabcd"
2. 通过加法来拼接
std::string str = "hello";
str += " world"; // 第一种加法
// str = str + "world"; // 第二种加法
简单比较下两种加法的开销:
#include <string>
#include <iostream>
#include <chrono>
void costTime(void(*f)(), const std::string& info)
{auto beforeTime = std::chrono::steady_clock::now();f();auto afterTime = std::chrono::steady_clock::now();double duration_millsecond = std::chrono::duration<double, std::milli>(afterTime - beforeTime).count();std::cout << info << " total cost " << duration_millsecond << "ms" << std::endl;
}int main () {costTime([](){std::string str;for (int i = 0; i < 10000; ++i) {str += "wwwwwww";} }, "+=");costTime([](){std::string str;for (int i = 0; i < 10000; ++i) {str = str + "wwwwwww";} }, "=");return 0;
}
执行结果:
+= total cost 0.167382ms
= total cost 128.886ms
由此可见,+=的方式效率要高出许多,所以在需要考虑这类性能的时候,尽量能使用+=就尽量使用。至于其中的原因:简单理解为,=的方式每次赋值前都需要创建一个和str几乎等同的(空间上)临时对象,也就是str的空间越大,随之而来的创建临时对象所需的时间就会越长,最终导致效率极低。
3. push_back方法
这个方法就比较简单了,就是在尾部追加一个字符,就不做介绍了。
4. insert方法
std::string str = "abc";
// 在指定位置插入字符串
str.insert(2, "ef");
assert("abefc" == str);// 在指定位置插入多个字符
str.insert(0, 3, 'x');
assert("xxxabefc" == str);// 在指定位置(迭代器)插入字符或多个字符
str.insert(str.begin() + 1, 'y');
str.insert(str.begin(), 2, 't');
assert("ttxyxxabefc" == str);// 在指定位置插入字符串的指定子串
// 插入的是从1的位置开始的3个字符
str.insert(0, "hgjkr", 1, 3);
assert("gjkttxyxxabefc" == str);
字符串删除
std::string str = "abcdefght";
// 从指定位置开始删除指定长度的字符
str.erase(2, 2);
assert("abefght" == str);// 从指定位置删除至末尾
str.erase(4);
assert("abef" == str);// 从指定位置(迭代器)删除至末尾
str.erase(str.begin() + 2);
assert("ab" == str);// 从指定开始位置(迭代器)删除至指定结束位置(迭代器)
// 左闭右开
str.erase(str.begin() + 1, str.end());
assert("a" == str);
字符串查找
1. find方法
std::string str = "abc bdef";
// 从左向右查找目标字符第一次出现的位置下标
int pos = str.find('b');
assert(pos == 1);
// 从左向右查找目标字符串第一次出现的位置下标
pos = str.find("bd");
assert(pos == 4);// 从指定位置开始查找目标字符或字符串
pos = str.find('b', 2);
assert(pos == 4);
如果目标字符或字符串不存在,则返回std::string::n_pos
;
简单测试下按字符查找和按字符串查找的效率:
int main() {costTime([](){std::string str = "abcdefgtbxyx";for (int i = 0; i < 100000; ++i) {str.find("x");} }, "str");costTime([](){std::string str = "abcdefgtbxyx";for (int i = 0; i < 100000; ++i) {str.find('x');} }, "char");
}
耗时如下,可以看出,同样查找x的位置,按字符的方式查找要比按字符串的方式查找更快。
str total cost 3.22321ms
char total cost 0.250851ms
2. rfind方法
参数同find,区别在于它的查找方向是从右向左。
std::string str = "abc bdef";
int pos = str.rfind('b');
assert(pos == 4);
3. find_first_of/find_first_not_of
std::string str = "abcfbcg";
// 从左向右查找第一次出现在目标字符串中的字符
// 字符c存在于目标字符串'fcg',所以返回pos=2
int pos = str.find_first_of("fcg");
assert(pos == 2);
对于pos = find_first_of("fcg")
可以简单理解为:
unsigned int pos1 = str.find('f');
unsigned int pos2 = str.find('c');
unsigned int pos3 = str.find('g');
unsigned int pos = std::min(pos1, pos2, pos3);
而find_first_not_of则正好相反,查找第一次不包含目标字符的位置。
std::string str = "abcdef";
int pos = str.find_first_not_of('abc');
assert(pos == 3);
4. find_last_of/find_last_not_of
使用方式同find_first_of/find_first_not_of,只不过查找方向是从右向左
std::string str = "abccfg";
int pos = str.find_last_of('cab');
assert(pos == 3);pos = str.find_last_not_of('cgf');
assert(pos == 1);
字符串替换
字符串替换使用replace
接口
std::string str = "hello world";
// 指定位置,指定长度的子串替换为目标字符串
// 从5的位置长度为1的子串(空格)替换为'::'
str.replace(5, 1, "::");
assert("hello::world" == str);// 指定位置,指定长度的子串替换为指定数目的目标字符
// 从5的位置长度为2的子串(::)替换为2个字符'!'
str.replace(5, 2, 2, '!');
assert("hello!!world" == str);// 指定位置,指定长度的子串替换为目标字符串中指定位置的子串
// 从5的位置长度为2的子串(!!)替换为目标串3的位置长度为2的子串'..'
str.replace(5, 2, "hjk..kl", 3, 2);
assert("hello..world" == str);// 替换为目标串0的位置开始长度为2子串'??'
// 此重载方法对于参数为const char*类型结果如下
str.replace(5, 2, "??##", 2);
assert("hello??world" == str);// 替换为目标串中2的位置开始到末尾的子串'##'
// 此重载方法对于参数为string类型的结果如下
std::string str2 = "??##";
str.replace(5, 2, str2, 2);
assert("hello##world" == str);// 迭代器起始位置到结束位置(不包含结束)替换为目标串
str.replace(str.begin() + 5, str.begin() + 7, "$$");
assert("hello$$world" == str);
如果想要实现python中替换目标字符或字符串的那种方式,得配合find方法,先找到目标串的位置,然后再进行替换。当然为了提高效率建议还是自己写一个。
字符串截取
std::string str = "abcdef";
// 从2的位置开始截取到末尾
std::string str2 = str.substr(2);
assert(str2 == "cdef");// 从0的位置开始截取长度为2的子串
str2 = str.substr(0, 2);
assert(str2 == "ab");
注意substr
不改变调用者自身,它会返回一个新的副本,也就是str始终没变。
字符串比较
std::string a = "abcd";
std::string b = "bcd";// 相等或者不相等可以直接通过==或!=来比较
assert(a != b);// 字符串a与b比较
int ret = a.compare(b);
assert(ret == -1);// 字符串a从1开始长度为3的子串与b比较
ret = a.compare(1, 3, b);
assert(ret == 0);// 字符串b从0开始长度为3的子串与a从1开始长度为3的子串比较
ret = b.compare(0, 3, a, 1, 3);
assert(ret == 0);
返回值有三种:1表示大于,-1表示小于,0为相等。
字符串与数字相互转换
1. 字符串转化为数字
stoi(const std::string& str, std::size_t* pos = nullptr, int base = 10)
- str:目标字符串
- pos:传出参数,用于接收最后一个非数字的字符位置
- base:进制,默认10进制,如果为0,则按照字符串格式进行解析
只传一个参数时:
std::string str = "123";
int num = std::stoi(str);
assert(num == 123);str = "123a";
num = std::stoi(str);
assert(num == 123);str = "a123";
// 此处会抛出异常invalid_argument
num = std::stoi(str);str = "12345678901";
// 会抛出异常out_of_range
num = std::stoi(str);
传入pos:
std::string str = "111ab1";
size_t pos = 0;
int num = std::stoi(str, &pos);
assert(num == 111 && pos == 3);str = "123";
num = std::stoi(str, &pos);
assert(num == 123 && pos == str.size());
- 可以看出,如果需要判断字符串是否完全是由数字组成,可以通过
pos == str.size()
来判断。
传入base:
std::string str = "111";
int num = std::stoi(str, nullptr, 2);
assert(num == 111b); // 二进制
num = std::stoi(str, nullptr, 8);
assert(num == 0111); // 八进制
num = std::stoi(str, nullptr, 16);
assert(num == 0x111); // 十六进制
num = std::stoi(str, nullptr, 0);
assert(num == 111);str = "0111";
num = std::stoi(str, nullptr, 0);
assert(num == 0111); // 8进制str = "0x111";
num = std::stoi(str, nullptr, 0);
assert(num == 0x111); // 16进制
其他转换方法:
转换方法 | 返回类型 |
---|---|
stol | long |
stoll | long long |
stoul | unsigned long |
stoull | unsigned long long |
stof | float |
stod | double |
stold | long double |
- 上述返回值为无符号整型时,如果传入的字符串是有符号的,依然会正常转换,只不过转换的结果是有符号数的无符号形式,比如-1的无符号形式是0xFFFFFFFF,对于32位整型而言。
- 对于浮点型转换,是没有进制这个参数的。
2. 数字转化为字符串
数字转化为字符串就相对简单了,使用std::to_string
即可,参数可以是任何数字类型。
字符串分割
分割的方法有很多:
- 通过find方法查找目标分隔符,然后利用substr去截取;
- 通过c函数strtok或strtok_r进行分割;
- 通过stringstream流。
下面只介绍其中一种,也就是stringstream流的方式:
#include <sstream>
#include <string>
#include <iostream>int main() {std::string str = " hello world I love you ";std::stringstream sstream(str);std::string buf;int cnt = 0;// 以空格分割while(std::getline(sstream, buf, ' ')) {if (buf.empty()) continue;std::cout << cnt++ << ":" << buf << std::endl;buf.clear();}return 0;
}
执行结果如下:
0:hello
1:world
2:I
3:love
4:you
注意一定要判断buf为空这种情况,否则第一次读到空格时,buf将会是一个空值。
相关文章:
C++中的string类操作详解
引言 针对C中的string,本文主要讲解如何对其进行插入、删除、查找、比较、截断、分割以及与数字之间的相互转换等。 字符串插入 1. append方法 std::string str "hello"; str.append(7, w); // 在末尾添加7个字符w str.append("wwwwwww");…...

Java绘图坐标体系
一、介绍 下图说明了Java坐标系。坐标原点位于左上角,以像素为单位。在Java坐标系中,第一个是x坐标,表示当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐…...
【MATLAB源码-第38期】基于OFDM的块状导频和梳状导频误码率性能对比,以及LS/LMMSE两种信道估计方法以及不同调制方式对比。
操作环境: MATLAB 2022a 1、算法描述 块状导频和梳状导频都是用于无线通信系统中信道估计的方法。 块状导频: 定义: 在频域上,块状导频是连续放置的一组导频符号。这意味着所有的导频符号都集中在一个短的时间段内发送。 优点…...

javaWeb车辆管理系统设计与实现
摘 要 随着经济的日益增长,车辆作为最重要的交通工具,在企事业单位中得以普及,单位的车辆数目已经远远不止简单的几辆,与此同时就产生了车辆资源的合理分配使用问题。 企业车辆管理系统运用现代化的计算机管理手段,不但可以对车辆的使用进行合理的管理,…...

【DM8】间隔分区
是范围分区的一个扩展 如果使用了间隔函数做分区,在数据插入的时候,如果没有合适的分区,数据库会自动创建一个新的分区。 –year往后推两年 SELECT SYSDATE numtoyminterval(2,‘YEAR’); –month往后推两年 SELECT SYSDATE numtoyminterv…...
0基础如何进入IT行业?
目录 0基础如何进入IT行业? 一、学习路径 二、技能培养 三、实践经验 0基础如何进入IT行业? 对于没有任何相关背景知识的人来说,成功进入IT行业可能看起来是一个遥不可及的目标。然而,只要有正确的方法和坚持不懈的努力&#…...

C#将Console写至文件,且文件固定最大长度
参考文章 将C#的Console.Write同步到控制台和log文件输出 业务需求 在生产环境中,控制台窗口不便展示出来。 为了在生产环境中,完整记录控制台应用的输出,选择将其输出到文件中。 但是,一次性存储所有输出的话,文件会…...

《CSS 知识点》仅在文本有省略号时添加 tip 信息
html <div ref"btns" class"btns"><div class"btn" >这是一段很短的文本.</div><div class"btn" >这是一段很短的文本.</div><div class"btn" >这是一段很长的文本.有省略号和tip.<…...

彩虹聚合DNS管理系统v1.0全新发布
聚合DNS管理系统(https://github.com/netcccyun/dnsmgr)可以实现在一个网站内管理多个平台的域名解析,目前已支持的域名平台有:阿里云、腾讯云、华为云、西部数码、CloudFlare。本系统支持多用户,每个用户可分配不同的…...
3.10 Python数据类型转换
Python类型转换,Python数据类型转换函数大全 虽然 Python 是弱类型编程语言,不需要像Java或 C 语言那样还要在使用变量前声明变量的类型,但在一些特定场景中,仍然需要用到类型转换。 比如说,我们想通过使用 print() …...
Kotlin基础学习
Kotlin基础学习主要涵盖安装Kotlin编译器、了解基础语法、学习变量声明、类型推断、函数定义以及控制结构等方面。以下是一个简要的Kotlin基础学习指南: 一、安装Kotlin 首先,你需要从JetBrains的官方网站下载并安装Kotlin编译器。同时,你也…...

配置交换机 SSH 管理和端口安全——实验1:配置交换机基本安全和 SSH管理
实验目的 通过本实验可以掌握: 交换机基本安全配置。SSH 的工作原理和 SSH服务端和客户端的配置。 实验拓扑 交换机基本安全和 SSH管理实验拓扑如图所示。 交换机基本安全和 SSH管理实验拓扑 实验步骤 (1)配置交换机S1 Switch>enab…...

海山数据库(He3DB)原理剖析:浅析Doris跨源分析能力
Doris湖仓分析背景: Doris多数据源功能演进 Doris的生态近年来围绕湖仓分析做了较多工作,Doris一直在积极拓宽大数据生态的OLAP分析市场,Doris2.0之后为了满足湖仓分析场景,围绕multi-catalog、数据缓存、容错、pipeline资源管理…...
第十三届蓝桥杯大赛软件赛省赛C/C++ 大学 B 组 题解
VP比赛链接 : 数据加载中... - 蓝桥云课 1 . 九进制 转 十进制 直接模拟就好了 #include <iostream> using namespace std; int main() {// 请在此输入您的代码int x 22*92*81*9;cout << x << endl ;return 0; } 2 . 顺子日期 枚举出每个情况即可 : …...

20240324-1-集成学习面试题EnsembleLearning
集成学习面试题 1. 什么是集成学习算法? 集成学习算法是一种优化手段或者策略,将多个较弱的模型集成模型组,一般的弱分类器可以是决策树,SVM,KNN等构成。其中的模型可以单独进行训练,并且它们的预测能以某…...

默克尔(Merkle)树 - 原理及用途
默克尔(Merkle)树的原理以及用途 引言 在当今数字化时代,确保数据的完整性是至关重要的。默克尔树作为一种高效的数据结构,被广泛应用于网络安全、分布式系统以及加密货币等领域,用于验证大量数据的完整性和一致性 数…...
设计模式:迭代器模式
迭代器模式的示例可以涵盖各种数据结构的遍历,包括数组、列表、树、图等。下面是一些不同场景下迭代器模式的示例及其代码实现。 示例 1: 数组遍历 使用迭代器模式遍历数组。 // 迭代器接口 interface Iterator<T> {boolean hasNext();T next(); }// 数组迭…...
Navicat Premium 16常用快捷键
打开一个新的查询窗口: Ctrl Q 关闭当前窗口: Ctrl W 运行当前窗口的SQL语句: Ctrl R 运行选中的SQL语句: Ctrl Shift R 注释选中的SQL语句: Ctrl / 取消注释SQL: Ctrl Shift / 保存连接&…...
LeetCode笔记——1042.不邻接植花
题目 有 n 个花园,按从 1 到 n 标记。另有数组 paths ,其中 paths[i] [xi, yi] 描述了花园 xi 到花园 yi 的双向路径。在每个花园中,你打算种下四种花之一。 另外,所有花园 最多 有 3 条路径可以进入或离开. 你需要为每个花园…...

Centos7搭建 Skywalking 单机版
介绍 Skywalking是应用性能监控平台,可用于分布式系统,支持微服务、云原生、Docker、Kubernetes 等多种架构场景。 整体架构如图 Agent :在应用中,收集 Trace、Log、Metrics 等监控数据,使用 RPC、RESTful API、Kafk…...

Flask RESTful 示例
目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题: 下面创建一个简单的Flask RESTful API示例。首先,我们需要创建环境,安装必要的依赖,然后…...
服务器硬防的应用场景都有哪些?
服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式,避免服务器受到各种恶意攻击和网络威胁,那么,服务器硬防通常都会应用在哪些场景当中呢? 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...
测试markdown--肇兴
day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...
五年级数学知识边界总结思考-下册
目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解:由来、作用与意义**一、知识点核心内容****二、知识点的由来:从生活实践到数学抽象****三、知识的作用:解决实际问题的工具****四、学习的意义:培养核心素养…...

高危文件识别的常用算法:原理、应用与企业场景
高危文件识别的常用算法:原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件,如包含恶意代码、敏感数据或欺诈内容的文档,在企业协同办公环境中(如Teams、Google Workspace)尤为重要。结合大模型技术&…...
拉力测试cuda pytorch 把 4070显卡拉满
import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试,通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小,增大可提高计算复杂度duration: 测试持续时间(秒&…...

零基础设计模式——行为型模式 - 责任链模式
第四部分:行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习!行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想:使多个对象都有机会处…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...
DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”
目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...
【Java学习笔记】BigInteger 和 BigDecimal 类
BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点:传参类型必须是类对象 一、BigInteger 1. 作用:适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...