7.【CPP】String类

一.汉字的编码

我们知道计算机存储英文字母,标点,数字用的是ascall码,128种用一个字节表示绰绰有余。而汉字远远不止128种,因此汉字需要两个字节表示

1.gbk编码中汉字占两个字节。
2.utf-8中,一个汉字占三个字节。

> UTF规定:如果一个符号只占一个字节,那么这个8位字节的第一位就为0。如果为两个字节,那么规定第一个字节的前两位都为1,然后第一个字节的第三位为0,第二个字节的前两位为10,然后如果是三个字节的话,那么第一个字节的前三位为111,第四位为0,剩余的两个字节的前两位都为10。按照这样的算法去思考一个中文字符的UTF-8是怎么表示的:一个中文字符需要两个字节来表示,两个字节一共是16位,那么UTF-8下,两个字节是不够的,因为两个字节下,第一个字节已经占据了三位:110,然后剩余的一个字节占据了两位:10,现在就只剩下8位,与Unicode下的两个字节,16位去表示任意一个字符是相悖的,也就是Unicode下的16位减去UTF-8下的8位=8位,刚好差了一个字节的空间,所以就使用三个字节去表示非ANSI字符:三个字节下,一共是24位,第一个字节头四位是:1110,后两个字节的前两位都是:10,那么24位-8位=16位,刚好两个字节去表示Unicode下的任意一个非ANSI字符

在这里插入图片描述

二.string类

参考string官方文档

1.扩容

在这里插入图片描述

这里resize和reserve都是在string对象后开指定大小的可用空间,而不是把它的容量设置为指定大小。
resize改变了size大小,而reserve不改变.
resize空间小于size大小时会删除元素,reserve空间小于size时不会改变容量也不删除元素。

2.小试牛刀

2.1把字符串转化为整数

迭代器实现

#include <cctype>
class Solution {
  public:
    int StrToInt(string str) {
        int ans = 0;
        int i=0;
        for (string::reverse_iterator rit = str.rbegin(); rit != str.rend(); ++rit) {
            
            if (isdigit(*rit)) {
                ans=ans+((*rit)-48)*pow(10,i++);
                continue;
            }
            if (*rit == '+')
                break;
            else if (*rit == '-')
                ans *= -1;
            else
                return 0;
        }
        return ans;
    }
};

2.2.Leetcode415.字符串相加

给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和并同样以字符串形式返回。

你不能使用任何內建的用于处理大整数的库(比如 BigInteger), 也不能直接将输入的字符串转换为整数形式。

示例 1:
输入:num1 = “11”, num2 = “123”
输出:“134”

class Solution {
public:
    string addStrings(string num1, string num2) {
        int end1=num1.size()-1,end2=num2.size()-1;
        int carry=0;
        string ans;
        //提前扩一波容
        ans.reserve(num1.size()>num2.size()?num1.size()+1:num2.size()+1);
        while(end1>=0||end2>=0)
        {
            int val1=end1>=0?num1[end1]-'0':0;
            int val2=end2>=0?num2[end2]-'0':0;
            int ret=val1+val2+carry;
            carry=ret/10;
            ans+=ret%10+'0';
            --end1;
            --end2;
        }
        if(carry)
            ans+='1';
        reverse(ans.begin(),ans.end());
        return ans;
    }
};

在这里插入图片描述

这里我们用数学中用竖式计算加法的方法,用carry表示进位,从后往前一步一步加到ans中,同时注意细节处理,最后反转一下ans得到答案。

3.find+replace(用xx替换xx)

在这里插入图片描述
这里我们用空格替换“/”,如果用多个字符替换呢?那么上述方法就显得很低效,因为不仅开空间还要往后挪数据。解决方法:以空间换时间,重新定义一个空字符,遍历两遍老字符。第一遍开空间,第二遍往空字符加数据。如下图
在这里插入图片描述

4.string类的swap和算法的swap

在这里插入图片描述
我们可以看到string的swap只需要改变指针指向就可以了,而std库里的需要创建一个临时对象还要拷贝赋值完成交换。

5.c_str()

将string转化为c语言能够识别的const char*,如下打开文件并读取
在这里插入图片描述

三.string类的写时拷贝(copy-on-write)

1.看下面这段代码

#include<stdio.h>
#include<string>

using namespace std;
int main()
{
  string s1("hello,world");
  string s2(s1);
  printf("%x\n",s1.c_str());
  printf("%x\n",s2.c_str());

  s1[2]='m';
  printf("%x\n",s1.c_str());
  printf("%x\n",s2.c_str());

  s2[2]='m';
  printf("%x\n",s1.c_str());
  printf("%x\n",s2.c_str());
  return 0;
}

下面代码在g++下运行结果
在这里插入图片描述
我们发现前面不修改s1和s2时两者地址是一样的,而修改s1中的字符时,操作系统此时才会给s1开辟一块独立的新空间把数据拷贝过去并修改。这就是所谓的写时拷贝技术。

2.简要理解如何实现?

每当我们为string分配内存时,我们总是要多分配一个空间用来存放这个引用计数的值,只要发生拷贝构造可是赋值时,这个内存的值就会加一。而在内容修改时,string类为查看这个引用计数是否为0,如果不为零,表示有人在共享这块内存,那么自己需要先做一份拷贝,然后把引用计数减去一,再把数据拷贝过来。

四.string类的模拟实现

class string {
	public:
		typedef char* iterator;
		iterator begin()
		{
			return _str;
		}

		iterator end()
		{
			return _str+_size;
		}

		string(const char* str = "") :
			_size(strlen(str))
		{
			_capacity = _size;
			_str = new char[_capacity + 1+1];
			strcpy(_str, str);
			
		}

		
		/*string(const string& s)
			:_size(s._size)
			, _capacity(s._capacity)
		{
			_str = new char[_capacity + 1];
			strcpy(_str, s._str);
		}*/

//现代写法
		void swap(string& tmp)
		{
			::swap(_str, tmp._str);
			::swap(_size, tmp._size);
			::swap(_capacity, tmp._capacity);
		}
		string(const string& s)
			:_str(nullptr)
			,_size(0)
			,_capacity(0)
		{
			string tmp(s._str);
			swap(tmp);
			_str[_capacity + 1]++;
		}


		~string()
		{
			delete[]_str;
			_str = nullptr;
			_size = _capacity = 0;
		}

		//实现赋值操作
		string& operator=(const string& s)
		{
			if (this != &s)
			{
				/*delete[] _str;
				_str = new char[strlen(s._str) + 1];
				strcpy(_str, s._str);*/
				char* tmp = new char[strlen(s._str) + 1];
				strcpy(tmp, s._str);

				delete[] _str;

				_str = tmp;
				_size = s._size;
				_capacity = s._capacity;
			}
			return *this;
		}

		const char* c_str()const
		{
			return _str;
		}

		size_t size()const
		{
			return _size;
		}

		size_t capacity()const
		{
			return _capacity;
		}

		char& operator[](size_t pos)
		{
			return _str[pos];
		}
		const char& operator[](size_t pos)const
		{
			assert(pos < _size);
			return _str[pos];
		}

		void reserve(size_t n)
		{
			if (n > _capacity)
			{
				char* tmp = new char[n + 1];
				strcpy(tmp, _str);
				delete[] _str;

				_str = tmp;
				_capacity = n;
			}
		}

		void push_back(char ch)
		{
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 0 : _capacity * 2);
			}

			_str[_size] = ch;
			++_size;
			_str[_size] = '\0';
		}

		void append(const char* str)
		{
			size_t len = strlen(str);
			if (_size + len > _capacity)
			{
				reserve(_size + len);
			}
			strcpy(_str + _size, str);
			_size += len;
		}

		string& operator+=(char ch)
		{
			push_back(ch);
			return *this;
		}

		string& operator+=(const char* str)
		{
			append(str);
			return *this;
		}

		string& insert(size_t pos, char ch)
		{
			assert(pos <= _size);
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}

			/*size_t end = _size;
			while (end >= pos)
			{
				_str[end + 1] = _str[end];
				--end;
			}*/

			size_t end = _size + 1;
			while (end > pos)
			{
				_str[end] = _str[end - 1];
				--end;
			}

			_str[pos] = ch;
			++_size;

			return*this;
		}

		string& insert(size_t pos, const char* str)
		{
			assert(pos <= _size);
			size_t len = strlen(str);
			if (_size + len > _capacity)
			{
				reserve(_size + len);
			}

			size_t end = _size + len;
			while (end >= pos + len)
			{
				_str[end] = _str[end + len];
				--end;
			}
			strncpy(_str + pos, str, len);
			_size += len;
		}

		void erase(size_t pos, size_t len = npos)
		{
			assert(pos < _size);

			if (len == npos || pos + len >= _size)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				strcpy(_str + pos, _str + pos + len);
			}
		}

		size_t find(char ch,size_t pos = 0)const
		{
			assert(pos < _size);
			for (size_t i = pos; i < _size; i++)
			{
				if (ch == _str[i])
				{
					return i;
				}
			}

			return npos;
		}

		size_t find(const char* sub, size_t pos = 0)const
		{
			assert(sub);
			assert(pos < _size);
			const char* ptr = strstr(_str + pos, sub);
			if (ptr == NULL)
			{
				return npos;
			}
			else
			{
				return ptr - _str;
			}
		}

		string substr(size_t pos, size_t len = npos)const
		{
			assert(pos < _size);
			size_t reallen = len;
			if (len == npos || pos + len > _size)
			{
				reallen = _size - pos;
			}

			string sub;
			for (size_t i = 0; i < reallen; i++)
			{
				sub += _str[pos + i];
			}
			return sub;
		}


	private:
		char* _str;
		size_t _size;
		size_t _capacity;


	public:
		static size_t npos;
	};
	size_t string::npos = -1;

	istream& operator>>(istream& in, string& s)
	{
		char ch;
		ch = in.get();

		const size_t N = 32;
		char buff[N];
		size_t i = 0;

		while (ch != ' ' && ch != '\n')
		{
			buff[i++] = ch;
			if (i=N-1)
			{
				buff[i] = '\0';
				s += buff;
				i = 0;
			}
			ch = in.get();
		}
		buff[i] = '\0';
		s += buff;
	}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java - 深入四大限流算法:原理、实现与应用

文章目录 Pre概述简单计数器原理实现测试优缺点 滑动窗口算法原理实现测试优缺点 漏桶算法原理实现测试优缺点 令牌桶算法原理实现测试优缺点 小结 Pre 深入理解分布式技术 - 限流 并发编程-25 高并发处理手段之消息队列思路 应用拆分思路 应用限流思路 SpringBoot - 优雅…

(上) C语言中的语句分类及分支语句:if语句、switch语句介绍

目录 前言 一、语句的分类 1. 空语句 2. 表达式语句 3. 函数调用语句 4. 复合语句 5. 控制语句 二、分支语句 1. if语句 (1) if的使用 (2) else的使用 (3) 分支中包含多条语句 (4) 嵌套if (5) 悬空else问题 2. switch语句 (1) if语句和switch语句的对比 (2) s…

摇臂MG995舵机模块实战教程

简介 舵机也叫伺服电机&#xff0c;最早用于船舶上实现其转向功能&#xff0c;由于可以通过程序连续控制其转角&#xff0c;因而被广泛应用智能小车以实现转向以及机器人各类关节运动中。舵机&#xff08;英文叫Servo&#xff09;&#xff1a;它由直流电机、减速齿轮组、传感器…

计算机网络——面试问题

1 从输⼊ URL 到⻚⾯展示到底发⽣了什么&#xff1f; 1. 先检查浏览器缓存⾥是否有缓存该资源&#xff0c;如果有直接返回&#xff1b;如果没有进⼊下⼀ 步⽹络请求。 2. ⽹络请求前&#xff0c;进⾏ DNS 解析 &#xff0c;以获取请求域名的 IP地址 。 3. 浏览器与服务器…

Sqoop与Kafka的集成:实时数据导入

将Sqoop与Kafka集成是实现实时数据导入和流处理的关键步骤之一。Sqoop用于将数据从关系型数据库导入到Hadoop生态系统中&#xff0c;而Kafka则用于数据流的传输和处理。本文将深入探讨如何使用Sqoop与Kafka集成&#xff0c;提供详细的步骤、示例代码和最佳实践&#xff0c;以确…

Git与GitHub零基础教学

大家好&#xff0c;我是星恒&#xff0c;这个帖子给大家分享的是git和github的全套教程&#xff0c;包含github初始&#xff0c;git常用命令以及基本使用&#xff0c;git的ssh链接github&#xff0c;github使用token登录&#xff0c;github和idea的配合&#xff0c;一些平时常用…

适合初学者的 机器学习 资料合集(可快速下载)

AI时代已经来临&#xff0c;机器学习成为了当今的热潮。但是&#xff0c;很多人在面对机器学习时却不知道如何开始学习。 今天&#xff0c;我为大家推荐几个适合初学者的机器学习开源项目&#xff0c;帮助大家更好地了解和掌握机器学习的知识。这些项目都是开源的&#xff0c;…

EtherNet/IP开发:C++开发CIP源代码

① 介绍一下CIP CIP是一种考虑到自动化行业而设计的通用协议。然而&#xff0c;由于其开放性&#xff0c;它可以并且已经应用于更多的领域。CIP网络库包含若干卷&#xff1a; 第1卷介绍了适用于所有网络自适应的CIP的常见方面。本卷包含通用对象库和设备配置文件库&#xff0…

信息安全的脆弱性及常见安全攻击

目录 信息安全概述信息安全现状及挑战传统安全防护逐步失效 安全风险能见度不足看不清资产看不见新型威胁看不见内网潜藏风险 常见的网络安全术语信息安全的脆弱性及常见安全攻击网络环境的开放性协议栈的脆弱性及常见攻击常见安全风险 协议栈自身的脆弱性网络的基本攻击模式 链…

Dubbo的几个序列化方式

欢迎订阅专栏&#xff0c;会分享Dubbo里面相关的技术实现 这篇文章就不详细的介绍每种序列化方式的实现细节&#xff0c;大家可以自行去问度娘&#xff0c;我也会找一些资料。需要注意的是&#xff0c;这个先后顺序不表示性能优越 ObjectInput、ObjectOutput 这两是Dubbo序列…

Linux_清理docker磁盘占用

文章目录 前言一、docker system 命令1. docker system df&#xff08;本文重点使用&#xff09;2. docker system prune&#xff08;本文重点使用&#xff09;3. docker system info4. docker system events 二、开始清理三、单独清理Build Cache四、单独清理未被使用的网络 前…

如何理解 GO 语言的接口 - 鸭子模型

个人认为&#xff0c;要理解 Go 的接口&#xff0c;一定先了解下鸭子模型。 鸭子模型 那什么鸭子模型&#xff1f; 鸭子模型的解释&#xff0c;通常会用了一个非常有趣的例子&#xff0c;一个东西究竟是不是鸭子&#xff0c;取决于它的能力。游泳起来像鸭子、叫起来也像鸭子…

【Emgu CV教程】5.6、几何变换之LinearPolar()极坐标变换

LinearPolar()线性极坐标转换函数用于将图像从笛卡尔坐标系转换为极坐标系&#xff0c;太难懂了&#xff0c;还是简单的说吧 笛卡尔坐标系就是平面直角坐标系&#xff0c;用X轴、Y轴表示的图像&#xff0c;最常用的表示方式&#xff0c;比如灰度图Point(360,100) 230&#xff…

数据结构与算法教程,数据结构C语言版教程!(第五部分、数组和广义表详解)四

第五部分、数组和广义表详解 数组和广义表&#xff0c;都用于存储逻辑关系为“一对一”的数据。 数组存储结构&#xff0c;99% 的编程语言都包含的存储结构&#xff0c;用于存储不可再分的单一数据&#xff1b;而广义表不同&#xff0c;它还可以存储子广义表。 本章重点从矩阵…

【工具使用】Keil5软件使用-基础使用篇

一、概述 本文面向未接触过Keil的新手&#xff0c;如果是职场老手可跳过此篇。为了快速上手&#xff0c;本文会跳过很多细节及解释&#xff0c;如需要了解原理&#xff0c;请移步进阶篇。 二、 软件介绍 Keil提供了包括C编译器、宏汇编、链接器、库管理和一个功能强大的仿真调…

【Git不走弯路】(二)提交与分支的本质

1. 前言 提交与分支是Git中两个基本对象&#xff0c;对初学者而言需要花些时间理解。正如我们之前所说&#xff0c;计算机中很多新概念是新瓶装旧酒。计算机技术来源于需求&#xff0c;服务于需求&#xff0c;需求是计算机技术的出发点和落脚点。梳理清楚工程实践中&#xff0…

【开源】基于JAVA的停车场收费系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 停车位模块2.2 车辆模块2.3 停车收费模块2.4 IC卡模块2.5 IC卡挂失模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 停车场表3.2.2 车辆表3.2.3 停车收费表3.2.4 IC 卡表3.2.5 IC 卡挂失表 四、系统实现五、核心代码…

2023.1.21 关于 Redis 主从复制详解

目录 引言 单点问题 分布式系统 ​​​​​​​​​​​​​​主从模式 配置 Redis 主从结构 断开主从关系 切换主从关系 补充知识点一 只读 网络延迟 拓扑结构 一主一从 一主多从 树形主从结构 主从复制的基本流程 数据同步 replicationid offset pzync 运…

transdata笔记:手机数据处理

1 mobile_stay_duration 每个停留点白天和夜间的持续时间 transbigdata.mobile_stay_duration(staydata, col[stime, etime], start_hour8, end_hour20) 1.1 主要参数 staydata停留数据&#xff08;每一行是一条数据&#xff09;col 列名&#xff0c;顺序为[‘starttime’,…

终极解决Flutter项目运行ios项目报错Without CocoaPods, plugins will not work on iOS or macOS.

前言 最近在开发Flutter项目&#xff0c;运行ios环境的时候报错没有CocoaPods&#xff0c;安卓环境可以正常运行&#xff0c;当时一脸懵逼&#xff0c;网上搜索了一下&#xff0c;有给我讲原理的&#xff0c;还有让我安装这插件那插件的&#xff0c;最终把电脑搞得卡死&#x…
最新文章