C++STL---模拟实现string

我们这篇文章进行string的模拟实现。

为了防止标准库和我们自己写的string类发生命名冲突,我们将我们自己写的string类放在我们自己的命名空间中:

我们先来搭一个class string的框架:

namespace CYF{
public://各种成员函数        
private:char _str;//存储字符串数组的指针size_t _size;//记录当前字符串的有效长度size_t _capacity;//记录当前字符串的容量static const size_t npos;//静态成员变量,很多地方的缺省值
}

默认成员函数

构造函数

		string(const char* str=""){_size = strlen(str);//初始字符串有效长度_capacity = _size;//初始字符串容量设为字符串有效长度_str = new char[_capacity + 1];//为存储字符串开辟空间,+1是为了保存'\0'strcpy(_str, str);//将str字符串拷贝到开好的空间}

拷贝构造函数

关于拷贝构造函数,我们首先需要了解一个知识点:深浅拷贝

浅拷贝就是拷贝出来的对象和原先的对象指向的是同一块空间,这样的话,其中一个对象对这块空间做了改变,也会影响另外一个对象。

深拷贝就是拷贝出来的对象跟原来的对象,指着的是两块不同的空间,两者相同指的是不同空间中的内容是相同的。

下图是深浅拷贝区别的形象化表现:

而在这里,显然我们并不想两者之间相互影响,所以我们要用到的是深拷贝。

所以我们要先开辟块容纳原有对象字符串的空间,然后将字符串拷贝过去,再将其他成员变量赋值过去即可,这是传统写法

		string(const string& str)//拷贝构造函数的传统写法:_size(0),_capacity(0),_str(new char[_capacity + 1]){strcpy(_str, str._str);_size = str._size;_capacity = str._capacity;}

我们还有一种现代写法

我们先根据原有字符串通过构造函数构造出一个tmp对象,然后再将tmp对象和拷贝对象的数据交换即可,这样的话,通过构造函数构造出来的tmp对象指向的空间和原对象的空间不同,并且交换之后,tmp是一个局部变量,出了作用域就会自动调用析构函数销毁,也就将tmp此时自身里拷贝对象原有的无用的数据全部删除了,一举两得:

		string(const string& str)//拷贝构造函数的现代写法:_str(new char[str._capacity+1]),_size(0),_capacity(0){string tmp(str._str);//调用构造函数,构造出一个C字符串为str._str的对象swap(tmp);//交换这两个对象,我们在后面会介绍}

关于拷贝构造函数我们还需要注意一点就是:传参的时候一定要传引用,如果传值的话,会再次调用拷贝构造函数,进而导致无限循环的调用拷贝构造函数。

赋值运算符重载

与拷贝构造函数一样,赋值运算符重载也涉及深浅拷贝问题,我们同样需要深拷贝,下面还是介绍传统和现代两种写法:

传统写法

我们首先要防止自己给自己赋值,然后释放原空间,开辟新空间,而后操作跟拷贝构造函数一样,最后返回值返回左值*this,以保证连续赋值。

		string& operator=(const string& str)//赋值运算符重载的传统写法{if (this != &str){delete[] _str;//释放原来的空间_str = new char[str._capacity + 1];//开辟新的空间strcpy(_str, str._str);//拷贝赋值_size = str._size;_capacity = str._capacity;}return *this;//返回左值(支持连续赋值)}

现代写法

也和拷贝构造函数的现代写法类似,只不过我们这里可以直接采取传值的方式传参,在传参的过程中拷贝构造出tmp对象,因为拷贝构造函数要防止无限调用拷贝构造函数的错误,所以必须采用引用传参,而这里我们只需要用传值传参即可:

string& operator=(string str)//赋值运算符重载的现代写法1
{swap(str);//交换两个对象return *this;//返回左值(支持连续赋值)
}

但是这样做的弊端就是无法防止为自己赋值,当我们使用上面的operator+给自己赋值的时候,虽然操作后,对象的_str指向的字符串的内容不变,但是字符串的地址发生了改变,我们想改变的话就用下面的写法:

		string& operator=(const string& str)//赋值运算符重载的现代写法2{if (this != &str)//防止给自己赋值{string tmp(str);//用str拷贝构造出对象tmpswap(tmp);//交换两个对象}return *this;//返回左值(支持连续赋值)}

析构函数

由于string内的成员对象_str指向一块从堆区开辟的空间,当对象销毁时,堆区对应的空间并不会自动销毁,所以为了避免内存泄漏,我们需要手动delete释放:

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

迭代器有关函数

string类的迭代器实际上就是字符指针,只是将char* typedef成iterator而已:

typedef char* iterator;
typedef const char* const_iterator;

begin && end

string中的begin和end函数实现的很简单:

	string::iterator string::begin(){return _str;//返回字符串第一个字符的地址}string::const_iterator string::begin()const{return _str;//返回字符串第一个字符的const对象的地址}string::iterator string::end(){return _str + _size;//返回'\0'的地址}string::const_iterator string::end()const{return _str + _size;//返回'\0'的const对象的地址             }

所以我们在这就明白了,用迭代器遍历string对象的时候,实际上就是在用指针遍历字符数组而已:

string::iterator it = s.begin();
while (it != s.end())
{cout <<*it;it++;
}
cout << endl;

而且,实际上,范围for本质上也是通过迭代器来工作的,在代码编译的时候,编译器会自动将范围for替换成迭代器的形式,所以说要有迭代器的容器才会支持范围for,我们此时已经实现了我们自己的string的迭代器,所以我们可以实现范围for的使用:

	for (auto& e : s){cout << e;}cout << endl;

与容量大小有关的函数

size && capacity

size()返回的是当前字符串的有效长度,capacity()返回的是字符串的容量:

		size_t size(){return _size;}size_t capacity(){return _capacity;}

直接将_size和_capacity返回即可。

reserve && resize

我们首先要对这两个函数做一下区分

我们先看reserve函数:

  • 当n大于对象当前capacity时,将capacity扩大到n或者大于n
  • 当n小于对象当前capacity时,什么操作都不做
		void reserve(size_t n)//若n>容量,才会扩容:则什么都不做{if (n > _capacity){char* tmp = new char[n + 1];//+1是为了放'\0'strncpy(tmp, _str, _size + 1);//为了防止对象中有有效的字符'\0',strcpy无法拷贝delete[]_str;_str = tmp;_capacity = n;}}

resize函数:

  • 当n小于当前_size时,将_size缩小到n
  • 当n大于当前_size时,将_size扩大到n,后面补的字符为c,c的缺省值为'\0'
		void resize(size_t n, char c = '\0'){if (n <= _size)//n小于_size{_size = n;//_size调整为n_str[_size] = '\0';//在第_size个字符后加\0}else{if (n > _capacity)//先看看是否用扩容{reserve(n);}for (size_t i = _size; i < n; i++)//将原先有效字符后直到第n个字符全都赋值成c{_str[i] = c;}_size = n;_str[_size] = '\0';//字符串后面放上\0}}

empty

判断string对象是否为空,我们比较两个字符串的时候使用strcmp来实现,使用strcmp函数时若两个字符串大小相等返回0,两个字符串比较的时候不能使用==。

		bool empty()//判断是否为空{return strcmp(_str, "") == 0;//两个字符串比较要用strcmp,不能直接用==}

修改字符串相关函数

push_back

push_back的作用就是尾插一个字母,我们需要先判断是否需要增容,然后再进行尾插,而且我们需要在该字符的后面设置'\0',否则打印字符串的时候就很可能会非法越界,因为尾插的字符后面不一定就是'\0'。

		void push_back(char c){if (_size == _capacity){reserve(_capacity == 0 ? 4 : 2 * _capacity);}_str[_size] = c;_str[_size + 1] = '\0';_size++;}

append

append的作用就是尾插一个字符串,我们依旧是需要先判断是否需要扩容,而后尾插。这里我们不需要在最后设置'\0',因为尾插的字符串最后自带'\0'。

		void append(const char* str){if (_capacity < _size + strlen(str))//若容量不够,则扩容{reserve(_size + strlen(str));}strcpy(_str + _size, str);_size += strlen(str);}

operator+=

operator+=的重载实现了字符串后面尾插字符和字符串的作用,我们可以直接调用上面实现的push_back和append函数:

		string& operator+=(const string& str)//传string对象{append(str._str);return *this;}string& operator+=(const char* str)//传C类型字符串{append(str);return *this;}string& operator+=(char c)//传一个字符{push_back(c);return *this;}

insert

insert函数的目的是在任意位置插入字符或字符串,我们首先要判断pos的合法性,而后判断capacity是否能容纳插入字符或字符串后的内容,若不能则调用reserve函数进行扩容,而后进行插入:

        //插入字符string& insert(size_t pos, char c){assert(pos <= _size);//检测pos是否合法if (_size == _capacity)//判断是否需要增容{reserve(_capacity == 0 ? 4 : 2 * _capacity);}size_t i = _size;while (i >= pos){_str[i + 1] = _str[i];i--;}_str[pos] = c;_size++;return *this;}//插入字符串string& insert(size_t pos, const char* str){assert(pos <= _size);//检测pos是否合法if (_size + strlen(str) > _capacity)//判断是否需要增容{reserve(_size + strlen(str));}char* end = _str + _size;while (end >= _str + pos){*(end + strlen(str)) = *end;end--;}strncpy(_str + pos, str, strlen(str));_size += strlen(str);return *this;}

我们要注意插入字符串的时候,要用strncpy,不能用strcpy,否则会将'\0'也拷贝进去。

erase

我们依然首先要判断pos是否合法,而后分两种情况进行操作。

我们这里只模拟实现下面这一种形式的erase函数:

string& erase (size_t pos = 0, size_t len = npos);

1.当pos位置及后面的有效字符都需要被删除时:

我们在pos位置上放置'\0'即可。

2.当pos位置及后面的有效字符只需要被删除一部分时:

我们将后面需要保留的有效字符覆盖前面需要删除的字符即可,此时也不用在字符串后面加'\0',因为字符串末尾有'\0'。

		string& erase(size_t pos, size_t len = npos){assert(pos < _size);//判断pos是否合法size_t n = _size - pos;if (len >= n)//说明pos后面的字符全部删除{_size = pos;_str[_size] = '\0';//字符串后面放上'\0'}else//说明pos后面还有一部分字符保留着{strcpy(_str + pos, _str + pos + len);//用需要保留的字符覆盖掉需要删除的字符_size -= len;}return *this;}

clear

clear函数用于将字符串清空

		void clear()//将对象中存储的字符串置空{_size = 0;_str[_size] = '\0';}

swap

这里的swap函数是我们用于交换两个对象的数据,我们直接调用库里的swap模板函数将对象的各个成员变量进行交换即可,但是这样的话我们就需要在swap函数前加上std::,告诉编译器这是在std中的swap函数,否则根据就近原则,编译器会以为是我们正在实现的swap函数。

		void swap(string& str)//交换两个string对象{std::swap(_size, str._size);//使用库函数std::swap(_capacity, str._capacity);std::swap(_str, str._str);}

c_str

用于获取string对象中的C类型字符串

		const char* c_str(){return _str;}

用于访问字符串的函数

operator[ ]

operator[ ]是为了让string对象能够通过下标的方式进行随机访问。

1.我们通过operator[ ]的方式可能会需要进行读取和修改操作

		char& operator[](size_t i)//可读可写{assert(i < _size);//检测下标的合法性return _str[i];}

2.某些场景下,我们只需要通过operator[ ]的方式读取字符而不冷修改。例如我们对一个const的string类对象进行[ ]+下标操作时就只能读,不能写。

		const char& operator[](size_t i)const//只读{assert(i < _size);//检查下标的合法性return _str[i];}

find

find函数用于正向查找一个字符或者字符串,返回找到的字符或者字符串下标

1、查找第一个字符

首先要判断pos的合法性,然后遍历的从前往后找目标字符,若找到返回下标,没找到,返回npos。

		size_t find(char c, size_t pos = 0)//正向寻找{assert(pos < _size);for (size_t i = pos; i < _size; i++)//从pos位置开始向后找目标字符{if (_str[i] == c){return i;}}return npos;}

2.查找第一个字符串

首先还是判断pos的合法性,然后我们通过strstr函数进行查找。strstr函数若找到了会返回目标字符串的起始地点,否则返回一个空指针。

		size_t find(const char* str, size_t pos = 0)//正向寻找{assert(pos < _size);const char* ret = strstr(_str + pos, str);//用strstr进行查找if (ret)//若找到子字符串,则返回子字符串的起始位置{return ret - _str;//返会字符串第一个字符的下标}else//找不到就返回nullptr{return npos;//返回npos}}

关系运算符重载>,<,<=,>=,==,!=

>,<,<=,>=,==,!=这六个关系运算符很好模拟,我们只写几个,剩下的复用其他的即可。

		bool operator>(const string& s)const{return strcmp(_str, s._str) > 0;}bool operator==(const string & s)const{return strcmp(_str, s._str) == 0;}bool operator<(const string& s)const{return !(this->operator>(s) || this->operator==(s));}bool operator!=(const string& s)const{return !(*this == s);}

<<,>>运算符重载及getline函数

>>运算符重载

>>运算符重载是为了让我们能够使用>>直接进行输入。输入前我们需要先将对象中的C字符串置空,然后从标准输入流中读取字符,直到读到' '或'\n'停止。

	std::istream& operator>>(std::istream& in, CYF::string& str){str.clear();//先清空字符串char ch = in.get();//读取一个字符while (ch != ' ' && ch != '\n')//若读取的字符不是空格或\n的话,尾插到str后面后继续读{str.push_back(ch);ch = in.get();}return in;//支持连续赋值}

<<运算符重载

这是为了我们能直接用<<运算符进行输出,我们直接进行遍历即可。

	std::ostream& operator<<(std::ostream& out, CYF::string& str){for (size_t i = 0; i < str.size(); i++){out << str[i];}return out;}

getline

getline函数用于读取一行含有空格的字符串。直到读到'\n'的时候停下来,其余跟operator>>一样。

	//getline跟>>基本相同,只不过是读取含有空格的字符串,知道读到\n的时候才停std::istream& getline(std::istream& in, CYF::string& str){str.clear();char ch = in.get();while (ch != '\n'){str.push_back(ch);ch = in.get();}return in;}

下面贴上完整代码:

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include <string>
#include <cassert>
#include <iostream>namespace CYF
{class string{public://string类的迭代器实际上就是字符指针typedef char* iterator;typedef const char* const_iterator;string(const char* str=""){_size = strlen(str);//初始字符串有效长度_capacity = _size;//初始字符串容量设为字符串有效长度_str = new char[_capacity + 1];//为存储字符串开辟空间,+1是为了保存'\0'strcpy(_str, str);//将str字符串拷贝到开好的空间}string(const string& str)//拷贝构造函数的传统写法:_size(0),_capacity(0),_str(new char[_capacity + 1]){strcpy(_str, str._str);_size = str._size;_capacity = str._capacity;}//string(const string& str)//拷贝构造函数的现代写法//	:_str(new char[str._capacity+1])//	,_size(0)//	,_capacity(0)//{//	string tmp(str._str);//调用构造函数,构造出一个C字符串为str._str的对象//	swap(tmp);//交换这两个对象//}~string(){delete[]_str;_str = nullptr;_size = 0;_capacity = 0;}string& operator=(const string& str)//赋值运算符重载的传统写法{if (this != &str){delete[] _str;//释放原来的空间_str = new char[str._capacity + 1];//开辟新的空间strcpy(_str, str._str);//拷贝赋值_size = str._size;_capacity = str._capacity;}return *this;//返回左值(支持连续赋值)}//string& operator=(string str)//赋值运算符重载的现代写法1//{//	swap(str);//交换两个对象//	return *this;//返回左值(支持连续赋值)//}//string& operator=(const string& str)//赋值运算符重载的现代写法2//{//	if (this != &str)//防止给自己赋值//	{//		string tmp(str);//用str拷贝构造出对象tmp//		swap(tmp);//交换两个对象//	}//	return *this;//返回左值(支持连续赋值)//}void swap(string& str)//交换两个string对象{std::swap(_size, str._size);//使用库函数std::swap(_capacity, str._capacity);std::swap(_str, str._str);}iterator begin();const_iterator begin()const;iterator end();const_iterator end()const;size_t size(){return _size;}size_t capacity(){return _capacity;}void reserve(size_t n)//若n>容量,才会扩容:则什么都不做{if (n > _capacity){char* tmp = new char[n + 1];//+1是为了放'\0'strncpy(tmp, _str, _size + 1);//为了防止对象中有有效的字符'\0',strcpy无法拷贝delete[]_str;_str = tmp;_capacity = n;}}void resize(size_t n, char c = '\0'){if (n <= _size){_size = n;_str[_size] = '\0';}else{if (n > _capacity){reserve(n);}for (size_t i = _size; i < n; i++){_str[i] = c;}_size = n;_str[_size] = '\0';}}bool empty()//判断是否为空{return strcmp(_str, "") == 0;//两个字符串比较要用strcmp,不能直接用==}void push_back(char c){if (_size == _capacity){reserve(_capacity == 0 ? 4 : 2 * _capacity);}_str[_size] = c;_str[_size + 1] = '\0';_size++;}void append(const char* str){if (_capacity < _size + strlen(str))//若容量不够,则扩容{reserve(_size + strlen(str));}strcpy(_str + _size, str);_size += strlen(str);}string& operator+=(const string& str){append(str._str);return *this;}string& operator+=(const char* str){append(str);return *this;}string& operator+=(char c){push_back(c);return *this;}string& insert(size_t pos, char c){assert(pos <= _size);//检测pos是否合法if (_size == _capacity)//判断是否需要增容{reserve(_capacity == 0 ? 4 : 2 * _capacity);}size_t i = _size;while (i >= pos){_str[i + 1] = _str[i];i--;}_str[pos] = c;_size++;return *this;}string& insert(size_t pos, const char* str){assert(pos <= _size);//检测pos是否合法if (_size + strlen(str) > _capacity)//判断是否需要增容{reserve(_size + strlen(str));}char* end = _str + _size;while (end >= _str + pos){*(end + strlen(str)) = *end;end--;}strncpy(_str + pos, str, strlen(str));_size += strlen(str);return *this;}string& erase(size_t pos, size_t len = npos){assert(pos < _size);//判断pos是否合法size_t n = _size - pos;if (len >= n)//说明pos后面的字符全部删除{_size = pos;_str[_size] = '\0';//字符串后面放上'\0'}else//说明pos后面还有一部分字符保留着{strcpy(_str + pos, _str + pos + len);//用需要保留的字符覆盖掉需要删除的字符_size -= len;}return *this;}void clear()//将对象中存储的字符串置空{_size = 0;_str[_size] = '\0';}const char* c_str(){return _str;}char& operator[](size_t i)//可读可写{assert(i < _size);//检测下标的合法性return _str[i];}const char& operator[](size_t i)const//只读{assert(i < _size);//检查下标的合法性return _str[i];}size_t find(char c, size_t pos = 0)//正向寻找{assert(pos < _size);for (size_t i = pos; i < _size; i++)//从pos位置开始向后找目标字符{if (_str[i] == c){return i;}}return npos;}size_t find(const char* str, size_t pos = 0)//正向寻找{assert(pos < _size);const char* ret = strstr(_str + pos, str);//用strstr进行查找if (ret)//若找到子字符串,则返回子字符串的起始位置{return ret - _str;//返会字符串第一个字符的下标}else//找不到就返回nullptr{return npos;//返回npos}}void reverse(iterator left, iterator right){right = right - 1;while (left < right){char c = '\0';c = *left;*left = *right;*right = c;left++;right--;}}//size_t rfind(char c, size_t pos = npos)//{//	string tmp(*this);//	reverse(tmp.begin(), tmp.end());//	if (pos > _size)//	{//		pos = _size - 1;//若pos大于等于字符串有效长度时,看作pos为字符串最后一个字符的下标//	}//	pos = _size - 1 - pos;//将pos改为镜像对称后的位置//	size_t ret = tmp.find(c, pos);//	if (ret != npos)//		return _size - 1 - ret;//若找到了,返回ret镜像对称后的位置//	else//		return npos;//若没找到,返回npos//}//size_t rfind(const char* str, size_t pos= npos)//{//	string tmp(*this);//拷贝构造对象tmp//	reverse(tmp.begin(), tmp.end());//逆置tmp的C字符串//	size_t len = strlen(str);//待查找的字符串长度//	char* arr = new char[len + 1];//开辟空间,用于拷贝待查找的字符串//	strcpy(arr, str);//	std::cout << arr << std::endl;//	//逆置待查找的字符串//	size_t left = 0;//	size_t right = len - 1;//	while (left < right)//	{//		std::swap(arr[left], arr[right]);//		left++;//		right--;//	}//	if (pos >= _size)//pos大于字符串有效长度,pos设为字符串最后一个字符的下标//	{//		pos = _size - 1;//	}//	pos = _size - 1 - pos;//将pos改为镜像对称后的位置//	size_t ret = tmp.find(arr, pos);//复用find函数正向查找//	delete[]arr;//	if (ret != npos)//	{//		return _size - ret - len;//找到了,返回ret再镜像逆置回去的位置//	}//	else//	{//		return npos;//没找到,返回npos//	}//}bool operator>(const string& s)const{return strcmp(_str, s._str) > 0;}bool operator==(const string & s)const{return strcmp(_str, s._str) == 0;}bool operator<(const string& s)const{return !(this->operator>(s) || this->operator==(s));}bool operator!=(const string& s)const{return !(*this == s);}private:char* _str;//存储字符串size_t _size;//字符串当前有效长度size_t _capacity;//当前字符串最大容量static const size_t npos;//整形最大值(很多地方的默认值)};const size_t string::npos =  (size_t) - 1;string::iterator string::begin(){return _str;//返回字符串第一个字符的地址}string::const_iterator string::begin()const{return _str;//返回字符串第一个字符的const地址}string::iterator string::end(){return _str + _size;//返回'\0'的地址}string::const_iterator string::end()const{return _str + _size;//返回'\0'的const地址             }std::istream& operator>>(std::istream& in, CYF::string& str){str.clear();//先清空字符串char ch = in.get();//读取一个字符while (ch != ' ' && ch != '\n')//若读取的字符不是空格或\n的话,尾插到str后面后继续读{str.push_back(ch);ch = in.get();}return in;//支持连续赋值}std::ostream& operator<<(std::ostream& out, CYF::string& str){for (size_t i = 0; i < str.size(); i++){out << str[i];}return out;}//getline跟>>基本相同,只不过是读取含有空格的字符串,知道读到\n的时候才停std::istream& getline(std::istream& in, CYF::string& str){str.clear();char ch = in.get();while (ch != '\n'){str.push_back(ch);ch = in.get();}return in;}}

大家可能会发现,我的代码中还实现了一个rfind,但是rfind函数我一直没找到错在了哪里,因为他会在析构函数处报内存泄漏的错误,如果大家发现哪里出错了,欢迎大家在评论区留言或私信我!!谢谢大家!

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

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

相关文章

基于单片机智能防触电装置的研究与设计

摘 要 &#xff1a; 针对潮湿天气下配电线路附近易发生触电事故等问题 &#xff0c; 对单片机的控制算法进行了研究 &#xff0c; 设 计 了 一 种 基 于 单片机的野外智能防触电装置。 首先建立了该装置的整体结构框架 &#xff0c; 再分别进行硬件设计和软件流程分析 &#xf…

IDEA升级web项目为maven项目乱码

今天将一个java web项目改造为maven项目。 首先&#xff0c;创建一个新的maven项目&#xff0c;将文件拷贝到新项目中。 其次&#xff0c;将旧项目的jar包&#xff0c;在maven的pom.xml做成依赖 接着&#xff0c;把没有maven坐标的jar包在编译的时候也包含进来 <build>…

实战教程:使用Go的net/http/fcgi包打造高性能Web应用

实战教程&#xff1a;使用Go的net/http/fcgi包打造高性能Web应用 简介理解FCGI协议FastCGI工作原理CGI与FastCGI对比其他接口对比 初步使用net/http/fcgi包设置和配置基础环境一个简单的FastCGI应用示例本地测试与调试 深入net/http/fcgi包的高级用法探索net/http/fcgi的主要功…

气膜建筑的运行保障:应对停电的解决方案—轻空间

气膜建筑作为一种现代化的建筑形式&#xff0c;以其独特的结构和多样的应用赢得了广泛关注。这种建筑依靠风机不断往内部吹气来维持其结构形态&#xff0c;那么如果遇到停电的情况&#xff0c;该如何确保其正常运行呢&#xff1f; 气膜建筑的供风系统 气膜建筑内部的气压维持依…

信创崛起:从安可到国产化,中国信息技术创新之路

在信息技术迅速演进的时代背景下&#xff0c;几个核心概念日益凸显其重要性&#xff1a;安全可靠&#xff08;简称安可&#xff09;、信息技术创新&#xff08;简称信创&#xff09;&#xff0c;以及国产化。这些概念紧密关联&#xff0c;共同服务于一个宏伟目标——构建一个独…

MFC 发起 HTTP Post 请求 发送MES消息

文章目录 获取Token将获取的Token写入JSON文件 将测试参数发送到http首先将测试参数写入到TestData.JSON文件rapidjson 库需要将CString 进行类型转换才能使用&#xff0c;将CString 转换为const char* 发送JSON 参数到http中&#xff0c;并且获取返回结果写入TestFinish.JSON文…

SpringSecurity6从入门到实战之SpringSecurity快速入门

SpringSecurity6从入门到实战之SpringSecurity快速入门 环境准备 依赖版本号springsecurity6.0.8springboot3.0.12JDK17 这里尽量与我依赖一致,免得在学习过程中出现位置的bug等 创建工程 这里直接选择springboot初始化快速搭建工程,导入对应的jdk17进行创建 直接勾选一个web…

Redhat9 LAMP安全配置方案及测试

目录 数据库主机 安装Mariadb数据库服务 设置mariadb开机自动启动 Php主机 部署Apache服务器 设置apache服务开机自启 安装php 安装 phpMyAdmin 打开测试机 更新软件包列表&#xff1a; 首先&#xff0c;确保你的软件包列表是最新的。打开终端并输入以下命令&#xf…

Linux查看设备信息命令

dmidecode | grep Product Name 查看grub版本号&#xff1a;rpm -qa | grep -i "grub" 客户端操作系统版本&#xff1a; cat /etc/issue cat /etc/redhat-release 处理器品牌及型号&#xff1a; less /proc/cpuinfo |grep model

【TCP协议中104解析】wireshark抓取流量包工具,群殴协议解析基础

Tcp ,104 ,wireshark工具进行解析 IEC104 是用于监控和诊断工业控制网络的一种标准&#xff0c;而 Wireshark则是一款常用的网络协议分析工具&#xff0c;可以用干解析TEC104 报文。本文将介绍如何使用 Wireshark解析 IEC104报文&#xff0c;以及解析过 程中的注意事项。 一、安…

AI图书推荐:用ChatGPT和Python搭建AI应用来变现

《用ChatGPT和Python搭建AI应用来变现》&#xff08;Building AI Applications with ChatGPT API&#xff09;将ChatGPT API与Python结合使用&#xff0c;可以开启构建非凡AI应用的大门。通过利用这些API&#xff0c;你可以专注于应用逻辑和用户体验&#xff0c;而ChatGPT强大的…

Axios的使用简单说明

axios 请求方式和参数 axios 可以发送 ajax 请求&#xff0c;不同的方法可以发送不同的请求: axios.get&#xff1a;发送get请求 axios.post&#xff1a;发送post请求 axios.put&#xff1a;发送put请求 axios.delete&#xff1a;发送delete请求 无论哪种方法&#xff0c;第一…

【2】:向量与矩阵

向量 既有大小又有方向的量叫做向量 向量的模 向量的长度 单位向量 (只表示方向不表示长度) 向量的加减运算 向量求和 行向量与列向量的置换 图形学中竖着写 向量的长度计算 点乘&#xff08;计算向量间夹角&#xff09; 点乘满足的运算规律 交换律、结合律、分配…

MouseBoost Pro for Mac v3.4.7 鼠标右键助手 安装教程【支持M芯片】

MouseBoost Pro for Mac v3.4.7 鼠标右键助手 安装教程【支持M芯片】 原文地址&#xff1a;https://blog.csdn.net/weixin_48311847/article/details/139201501

Kibana创建ElasticSearch 用户角色

文章目录 1, ES 权限参考2, 某应用的管理员权限&#xff1a;可以open/close/delete/cat/read/write 索引3, 某应用的读写权限&#xff1a;可以cat/read/write 索引 &#xff08;不能删除索引或数据&#xff09;4, 某应用的只读权限 1, ES 权限参考 https://www.elastic.co/gui…

Notepad++不显示CRLF的方法

View -> Show Symbol -> 去掉勾选 Show All Characters

【教程】PaddleOCR高精度文字识别

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ PaddleOCR/doc/doc_ch/quickstart.md at main PaddlePaddle/PaddleOCR GitHub 安装 pip install paddlepaddle -i https://mirror.baidu.com/pypi/s…

新版idea配置git步骤及项目导入

目录 git安装 下载 打开git Bash 配置全局用户名及邮箱 查看已经配置的用户名和邮箱 在IDEA中设置Git 问题解决 项目导入 git安装 下载 进入官网 Git - Downloads 点击所属本机系统&#xff0c;window如下图 选择64位安装 按照默认步骤一直下一步即可 打开git Bash …

HackTheBox-Machines--Beep

Beep测试过程 1 信息收集 nmap端口扫描 gryphonwsdl ~ % nmap -sC -sV 10.129.137.179 Starting Nmap 7.94 ( https://nmap.org ) at 2024-05-28 14:39 CST Nmap scan report for 10.129.229.183 Host is up (0.28s latency). Not shown: 988 closed tcp ports (conn-refused…

Nacos 2.x 系列【12】配置加密插件

文章目录 1. 前言2. 安装插件2.1 编译2.2 客户端2.3 服务端 3. 测试 1. 前言 为保证用户敏感配置数据的安全&#xff0c;Nacos提供了配置加密的新特性。降低了用户使用的风险&#xff0c;也不需要再对配置进行单独的加密处理。 前提条件&#xff1a; 版本:老版本暂时不兼容&…