C++的STL标准模板库容器--string类

news2024/11/24 4:46:09

目录

浅浅介绍一下什么是STL:

 string类需要知道的小知识

auto和范围for:

string类的常用接口:

实现一个string类:

1. 成员变量和构造函数,拷贝构造,析构函数

2. string类对象的容量操作

<1>size,capacity,empty,clear(初始化)

<2>reserve(扩容),resize

3. string类对象的访问和遍历

4. string类对象的修改操作

<1>push_back(尾插), append, operator+=

<2>insert(插入),erase(删除),operator=(赋值)

<3>c_str(返回C格式字符串)和find(查找)

<4>substr(复制子字符串)

5. string类非成员函数


浅浅介绍一下什么是STL:

STL(standard template libaray-标准模板库):是C++标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架。STL有六大组件:仿函数,算法,迭代器,空间适配器,容器,配接器
本篇讲的就是STL容器部分的string类

 string类需要知道的小知识

 auto和范围for:

(1)在本篇的学习中将使用到关键字auto,它的作用是自动识别类型

注意:

1.使用auto声明指针类型,auto和auto*没有任何区别,但如果修饰引用类型则必须加上&

2.auto在对同一行声明多个变量时,变量必须是相同的类型,否则会报错,因为编译器实际只能对第一个类型进行推导,然后用推导出来的类型定义其他变量

//会出现编译报错:在声明符列表中,auto必须始终推导为同一类型
auto a = 1, b = 2.1;//在这之前已经推导出了int

3.auto不能够作为函数的参数,但可以作为返回值,想设为返回值请谨慎使用

4.auto不能设为数组

(2)范围for

C++11中引入了基于范围的for循环,for循环后的括号由冒号“ :”分为两部分:第一部分是范围
内用于迭代的变量,第二部分则表示被迭代的范围,自动迭代,自动取数据,自动判断结束。

1.对于一个知道范围的集合而言,就可以使用范围for

2.范围for可以作用到数组和容器上进行遍历

3.范围for的底层实际上就是替换为迭代器

int main()
{
    int a[]={1,2,3,4,5};
    for(auto& a1:a)
    {
        cout<<a1<<" ";
    }
    return 0;
}

string类的常用接口:

string是一个比较早的容器所以在部分设计上会出现冗余的情况,我只会讲一些比较常用的接口,其他的接口请自行查看文档

string类接口详细文档传送门:string - C++ 参考 (cplusplus.com)

1.string类对象的常见构造

string()        //构造出一个空的string对象

string(const char* ch)        //使用一个字符串进行构造对象

string  (size_t n,char c)           //string类对象中包含n个字符c,这个基本不实现

string  (const string& st)        //拷贝构造

2.string类对象的容量操作

size                   //返回字符串有效长度

capacity           //返回空间总大小

empty               //判断字符串是否为空            

clear                 //清空字符串       

reserve             //空间扩容        

resize               ​​​​​​//将字符串大小调整为n个字符的长度,多出的空间用字符c填充

3.string类对象的访问和遍历

operator[]         //返回pos位置的字符引用                      

begin                //获取字符串起始位置字符的迭代器

end                   //获取字符串最后一位的下一位置的迭代器

4.string类对象的修改操作

operator=             //将一个对象赋值给另一个对象

push_back         //尾插一个字符或者字符串

append               //在字符串后追加一个字符串

operator+=         //在字符串后面追加一个字符或者字符串

c_str                   //返回C格式字符串

find+pos             //在起始位置加上pos后的下标开始往后查找字符或者字符串出现的起始位置

substr                 //从对象的字符串的pos位置开始向后截取n个字符,然后返回

insert                   //在pos位置上插入一个字符或者字符串

5.string类非成员函数

operator>>          //输入重载

operator<<          //输出重载

实现一个string类:

这里都是我自己实现各项功能,实现参数部分可能会和文档有差异,请见谅

1. 成员变量和构造函数,拷贝构造,析构函数
string类主要有三个成员变量,还会设置一个静态成员常量,分别是指向数组的指针char*,记录有效字符数的_size,和容量_capacity,静态成员常量npos

构造函数上我们不使用初始化列表,参数设成一个带有缺省值的char*指针,这样可以同时解决单字符初始化,字符串初始化,空字符串对象三种初始化

注意:

1.npos固定为-1,因为在参数里会使用它作为缺省值,无符号整形的符号位无效所以当缺省值有效时它的大小是:4294967295。它的作用就是给一个很大的值,在后面的函数实现中erase(删除)和substr(复制子字符串)会使用到

2.字符串的结尾都会跟上一个'\0'它也是占空间的,但记录容量的_capacity并不会记录这个空间,所以在new开空间的时候要额外增加一个空间给'\0',如果不开这个空间就有两个结果:

<1>不在字符串尾部增添'\0'导致循环越界访问直至遇到'\0';

<2>增添'\0'的位置已经被别的变量申请走了,越界增添'\0'导致破坏了其他资源的完整性        

class string
{
public:
    string(const char*ch="")    //注意这里给的缺省值是没有有效字符的哦
    {
        size_t size=strlen(ch);
        _str=new char[size+1];  //加1为'\0'开空间
        strcpy(_str,ch);
        _size=_capacity=size;
    }
private:
	char* _str;
	size_t _size;
	size_t _capacity;
	static const int npos = -1;
}

拷贝构造现在也不应该是浅拷贝,因为出现了空间申请,需要使用深拷贝,现在我们实现一个swap(交换函数)来辅助进行拷贝构造的实现。

注意:直接使用库里的swap对对象(参数是对象)也可以完成交换,但是和我自己实现交换过程有很大差别,库里的实现是对对象的拷贝再交换,这样会导致频繁的触发拷贝构造(深拷贝)导致降低效率,所以不直接交换对象,交换成员变量

出现了资源申请析构函数也不能使用自动生成的了,需要自己实现

void swap(string& ch)
{
	std::swap(_str, ch._str);            //交换指针
	std::swap(_size, ch._size);          //交换大小
	std::swap(_capacity, ch._capacity);  //交换容量
}
string(const string& str)
{
    //std::swap(*this,str);   频繁触发拷贝构造会出现效率降低的问题
	string temp(str._str);    //这里是用str的字符串初始化一个string对象
	swap(temp);               //现在交换temp和this指针指向的对象
}
~string()
{
    if(!empty())
    {
	    delete[] _str;
	    _str = nullptr;
	    _size = _capacity = 0;
    }
}
2. string类对象的容量操作
<1>size,capacity,empty,clear(初始化)

这四个都是比较简单的成员函数,直接给大家看实现,一看很快就能理解了

size_t size()        //返回有效字符个数
{
    return _size;
}
size_t capacity()    //返回容量大小
{
    return _capacity;
}
bool empty()         //判空
{
    return _size==0;
}
void clear()
{
    _str[0]='\0';    //这里要重载[]返回当前位置的引用才能直接修改
    _size=0;
}
<2>reserve(扩容),resize

剩下的这两个,reserve负责扩容比较耳熟能详;resize它能调整字符串的长度,如果给的n大于当前字符串长度,则通过在末尾插入所需数量的字符来扩展,直到达到n的长度,如果给的n小于当前长度,那么缩短为当前n个字符,并且删除第n个字符(包括n)以外的字符

下面直接看实现来理解更方便:

void reserve(size_t n)
{
    if(n>_capacity)
    {
        char* newstr=new char[n+1];    //这里也不要忘了'\0'的位置
        strcpy(newstr,_str);           //拷贝一下旧空间
        delete[] _str;                 //删除旧空间
        _str=newstr;                   //指向新空间
        _capacity=n;                   //更新容量 
    }
}
void resize(size_t n, char c='0')    
{
	if (n > _capacity)                 //如果容量不够就扩容
	{
		reserve(_capacity * 2);
	}
	if (n < size())                    
	{
		_str[n] = '\0';                //小于的话直接在n位置加'\0'
		_size = n;                     //更新有效字符数
	}
	else
	{
		for (int i = size(); i <= n; i++)    
		{
			_str[i] = c;     
		}
        _size=n;            //更新_size
        _str[_size]='\0';   //不要忘了'\0'
	}
}
3. string类对象的访问和遍历

这里就会涉及到迭代器的使用了。迭代器的主要目的是为了屏蔽底层细节,让代码有更好封装性和模块化,能够让使用者不要直接面对复杂的底层内部实现,只需要通过简化后的接口进行操作,使其更容易使用和理解,它就是封装的一种体现方式

class string
{
public:
	typedef char* iterator;    //自定义命名char*
	iterator begin()    //返回字符串起始位置的迭代器
	{
		return _str;    
	}
	iterator end()     //返回字符串末尾下一位的迭代器
	{
		return _str+_size;
	}
    const iterator begin()const    //const迭代器
    {
	    return _str;
    }
    const iterator end()const
    {
	    return _str + _size;
    }
}

operator[]:重载方括号方便字符串的访问和修改

char& operator[](size_t pos)    //运算符重载
{
    assert(pos < _size);        //当前下标必须有效
    return _str[pos];           //返回当前下标字符的引用 
}
4. string类对象的修改操作
<1>push_back(尾插), append, operator+=

这三个成员函数在实现上都有着十分相似的情况,甚至可以相互复用所以直接放到一起讲

void push_back(const char ch)
{
	if (_size == _capacity)    //判断容量是否足够
	{
		reserve(_capacity == 0 ? 4 : _capacity * 2);
	}	
    _str[_size++] = ch;    //尾插一个字符
	_str[_size] = '\0';
}

string& operator+=(const char ch)    //复用push_back完成尾插一个字符
{
	push_back(ch);        
	return *this;
}

string& operator+=(const char* ch)
{
	size_t len = strlen(ch);        //计算字符串长度
	if (_size+len > _capacity)      //判断容量是否足够
	{
		reserve(_size + len > _capacity * 2 ? _size + len : _capacity * 2);
	}
	memcpy(_str + _size, ch, sizeof(char) * len);    //拷贝空间
	_size += len;                                    //更新_size
	_str[_size] = '\0';                              //尾部增添'\0'  
	return *this;        
}

void append(const char* str)      //在字符串后面增添字符串
{
	*this+=str;    //调用operator+=
}

void push_back(const char* ch)    //这个和append就一样
{
	*this += ch;    //调用operator+=
}

在实现完后就可以发现这些函数的功能重叠了好多,append和push_back尾插字符串功能就重叠了,这就是因为比较早出现的原因出现的设计冗余

<2>insert(插入),erase(删除),operator=(赋值)

insert(插入)在代码上容量扩容方面和上面基本一致,但注意不要把insert和前面的功能搞混,insert是插入字符串中的有效字符,上面的三个都是尾插需要在末尾手动添加'\0',假设调用insert时pos位置是尾巴,在代码段末端增加'\0'的代码那就和上面的功能无异,那pos在中间时那不就暴毙了,搁中间加'\0'那就错了,这也是为什么尾插无法复用insert,所以单独拉出来讲

void insert(size_t pos, char ch)    //在pos位置插入一个字符
{
	assert(pos <= _size);      //pos位置必须有效
	if (_size == _capacity)     //判断容量是否足够
	{
		reserve(_capacity == 0 ? 4 : _capacity * 2);
	}
	size_t end = _size + 1;    //找到'\0'位置的下一个位置
	while (end >= pos)         //把pos位置后的字符(包括'\0')全部后移
	{
		_str[end] = _str[end - 1];
		end--;
	}
	_str[pos] = ch;            //插入字符
	_size++;                   //_size加1 
}

void insert(size_t pos, const char* ch)
{
	assert(pos <= _size);            //pos位置必须有效
	size_t len = strlen(ch);         //计算插入字符串长度
	if (_size + len > _capacity)     //容量大小是否足够
	{
		reserve(_size + len > _capacity * 2 ? _size + len : _capacity * 2);
	}
	int end = _size + len;           //'\0'位置加上字符串长度  
	while (end >= pos + len )        //将包括'\0'的字符全部后移
	{
		_str[end] = _str[end - len];
		end--;
	}
	memcpy(_str + pos, ch, sizeof(char) * len);    //直接拷贝进去
	_size += len;                    //_size增加插入字符串长度
}

erase(删除)的主要作用:要求在pos位置开始向后删除len个字符,len有缺省值,当缺省值有效时删除pos位置包括pos位置往后的所有字符,因为len此时超级大

void erase(size_t pos, int len=npos)
{
	assert(pos < _size);        //pos位置必须有效
	if (len >= _size - pos)     //如果删除字符数大于剩下字符数
	{
		_str[pos] = '\0';       //直接pos位置给'\0'
		_size = pos;            //更新_size
	}
	else
	{
		for (size_t i = pos + len; i <= _size; i++)    //将pos+len的数据前移
		{ 
			_str[i - len] = _str[i];
		}
		_size -= len;    //更新_size
	}
}

operator=(赋值):这个就很简单了,使用赋值对象构造一个对象,然后交换被赋值对象和构造出来的对象即可

string& operator=(string temp)    //这里不用引用直接传参触发拷贝构造
{
	swap(temp);                   //交换(形参不影响实参)
	return *this;
}
<3>c_str(返回C格式字符串)find(查找)

在没有重载输出(cout)之前 cout<<st1(string类对象) 肯定是不行的,因为它是一个对象并不是字符串,c_str就是专门解决这个问题,它会返回字符串的起始位

const char* c_str()
{
	return _str;
}
//没重载cout之前 cout<<st1<<endl; 无法打印字符串
//cout<<st1.c_str()<<endl; 这样就可以了

find(查找):返回在pos位置往后查找字符或者字符串出现的第一位置的下标,

查找一个字符:直接for循环遍历查找,找到返回下标

查找字符串:调用strstr函数直接查找字符串出现第一个位置的指针,通过这个指针减去起始位就是下标

下面看实现:

size_t find(char ch, size_t pos)
{
	assert(pos < _size);        //pos位置必须有效
	for (size_t i = pos; i < _size; i++)
	{
		if(_str[i]==ch)
		{
			return i;          //返回下标
		}
	}
}
	
size_t find(const char* ch, size_t pos)
{
	assert(pos < _size);
	const char* fin = strstr(_str + pos, ch);  //找到并返回出现的第一个位置的指针
	if (fin == nullptr)        //空代表没找到
	{
		return npos;           //返回npos
	}
	else
	{
		return fin - _str;     //当前指针减去起始位置得出下标返回
	}
}
<4>substr(复制子字符串)

substr的主要作用:要求指定位置开始往后截取n个字符并返回,如果n没有给指定的长度那么缺省值有效,默认从指定位置开始向后截取全部字符返回,下面看实现理解更方便

string substr(size_t pos, size_t len=npos)
{
	assert(pos < _size);        //依旧是pos位置必须有效
	//len大于剩余长度,更新len
	if (len > _size-pos)        
	{
		len = _size-pos;
	}
	string sub;                        //构造一个新的string类对象
	sub.reserve(len);                  //给len长度的空间
	for (size_t i = 0; i < len; i++)   //从pos位置开始向后截取len次字符 
	{
		sub += _str[pos + i];          //尾插
	}
	return sub;    //返回当前类对象
}
5. string类非成员函数

比较主要的就是重载输入和输出了,这个比较简单直接看实现来理解

ostream& operator<<(ostream& out, const string& s)
{
    for(auto ch:s)    //使用范围for自动识别类型,自动迭代,自动判断停止
    {
        cout<<ch;
    }
    out << endl;
    return out;
}
istream& operator>>(istream& in, string& s)
{
    s.clear();        //输入自然要对当前对象进行初始化
    char ch;
    ch=in.get()
    while(ch != ' ' && ch != '\n')    //这个是判断什么时候停下来,我假设
    {                                 //遇到空格和换行就停止
        s+=ch;
        ch=in.get();
    }
    return in; 
}

本篇文章到这里主要内容已经讲完了,希望能够对你产生帮助,感谢阅读

 需要看全部实现可以往下翻下面有完整实现


我自己实现的一个string类 :

//头文件
#include <iostream>
#include <assert.h>
using namespace std;

namespace cool
{
	class string
	{
	public:
		//简易迭代器
		typedef char* iterator;
		iterator begin()               //普通迭代器
		{
			return _str;
		}
		iterator end()
		{
			return _str+_size;
		}
		const iterator begin()const    //const迭代器
		{
			return _str;
		}
		const iterator end()const      
		{
			return _str + _size;
		}
		string(const char*str="")      //构造
		{
			_size = strlen(str);
			_capacity = _size;
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}		    
		~string()                      //析构     
		{
			delete[] _str;
			_str = nullptr;
			_capacity = _size=0;
		}
		void swap(string& str)         //交换函数
		{
			std::swap(_str, str._str);
			std::swap(_size, str._size);
			std::swap(_capacity, str._capacity);
		}
		string(const string& str)      //拷贝构造
		{
			string temp(str._str);
			swap(temp);
		}
		const char* c_str()const       //返回C格式字符串
		{
			return _str;
		}
		size_t size()const             //返回有效个数
		{
			return _size;
		}
        bool empty()                   //判空
        {
            return _size==0;
        }
		void clear()                   //初始化
		{
			_str[0] = '\0';
			_size = 0;
		}
		void reserve(size_t n)         //扩容
		{
			if (n > _capacity)
			{
				char* temp = new char[n+1];
				strcpy(temp, _str);
				delete[]_str;
				_str = temp;
				_capacity = n;
			}
		}
        void push_back(char ch);                  //尾插
        void push_back(const char* ch);          //尾插的函数重载

		string& operator+=(const char ch);        //重载+=
		string& operator+=(const char* ch);       //+=的函数重载
		string& operator=(string temp);           //重载=

        char& operator[](size_t pos)              //重载[]
		const char& operator[](size_t pos)const;  //const类型的[]重载

		void append(const char* str);             //字符串后增加一个字符串

		void insert(size_t pos, char ch);         //插入
		void insert(size_t pos, const char *ch);  //插入的函数重载  
		void erase(size_t pos,int len=npos);      //删除  

		size_t find(char ch, size_t pos = 0);           //查找 
		size_t find(const char* ch, size_t pos = 0);    //查找的函数重载
		string substr(size_t pos = 0, size_t len=npos); //复制一个子字符串   
	private:

		char* _str=nullptr;
		size_t _size=0;
		size_t _capacity=0;
		static const size_t npos=-1;
	};

    //重载输入输出
	ostream& operator<<(ostream& out, const string& s);
	istream& operator>>(istream& in, string& s);

    void test();
    void test1();
    void test2();
    void test3();
    void test4();
}
//实现文件
namespace cool
{
	void string::push_back(const char ch)
	{
		if (_size == _capacity)
		{
			reserve(_capacity == 0 ? 4 : _capacity * 2);
		}
		_str[_size++] = ch;
		_str[_size] = '\0';
	}

	void string::push_back(const char* ch)
	{
		*this += ch;
	}

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

    string& string::operator+=(const char* ch)
    {
	    size_t len = strlen(ch);
	    if (_size+len > _capacity)
	    {
	    	reserve(_size + len > _capacity * 2 ? _size + len : _capacity * 2);
	    }
	    memcpy(_str + _size, ch, sizeof(char) * len);
	    _size += len;
	    _str[_size] = '\0';
	    return *this;
    }

    string& string::operator=(string temp)
    {
    	swap(temp);
    	return *this;
    }
    
    char& string::operator[](size_t pos)
	{
		assert(pos < _size);
		return _str[pos];
	}

	const char& string::operator[](size_t pos)const
	{
		assert(pos < _size);
		return _str[pos];
	}
    
    void string::append(const char* str)
    {
	    *this+=str;    
    } 
    
	void string::insert(size_t pos, char ch)
	{
		assert(pos <= _size);
		if (_size == _capacity)
		{
			reserve(_capacity == 0 ? 4 : _capacity * 2);
		}
		size_t end = size() + 1;
		while (end >= pos)
		{
			_str[end] = _str[end - 1];
			end--;
		}
		_str[pos] = ch;
		_size++;
	}

	void string::insert(size_t pos, const char* ch)
	{
		assert(pos <= _size);
		size_t len = strlen(ch);
		if (_size + len > _capacity)
		{
			reserve(_size + len > _capacity * 2 ? _size + len : _capacity * 2);
		}
		size_t end = _size + len;
		while (pos + len <= end)
		{
			_str[end] = _str[end - len];
			end--;
		}
		memcpy(_str + pos, ch, sizeof(char) * len);
		_size += len;
	}

	void string::erase(size_t pos, size_t len)
	{
		assert(pos <= _size);
		if (len >= _size - pos)
		{
			_str[pos] = '\0';
			_size = pos;
		}
		else
		{
			for (size_t i = pos + len; i <= _size; i++)
			{
				_str[i - len] = _str[i];
			}
			_size -= len;
		}
	}

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

	size_t string::find(const char* ch, size_t pos)
	{
		assert(pos < _size);
		const char* ptr = strstr(_str + pos, ch);
		if (ptr == nullptr)
		{
			return npos;
		}
		else
		{
			return ptr - _str;
		}
	}
    
    string string::substr(size_t pos, size_t len)
	{
		assert(pos < _size);
		if (len >_size - pos)
		{
			len = _size - pos;
		}
		string sub;
		sub.reserve(len);
		for (size_t i = 0; i <len; i++)
		{
			sub += _str[pos + i];
		}
		return sub;
	}

	ostream& operator<<(ostream& out, const string& s)
	{
		for (auto ch : s)
		{
			out << ch;
		}
		return out;
	}

	istream& operator>>(istream& in, string& s)
	{
		s.clear();
		const int N = 256;
		char buff[N];
		int i=0;
		char ch;
		ch = in.get();
		while (ch != '\n')
		{
			buff[i++] = ch;
			if (i == N - 1)
			{
				buff[i] = '\0';
				s += buff;
				i = 0;
			}
			ch = in.get();
		}
		if (i > 0)
		{
			buff[i] = '\0';
			s += buff;
			i = 0;
		}
		return in;
	}  
    //测试用例
    void test( )
    {
	    string s1;
	    string s2("h");
	    string s3("hello");
	    cout << s1 << endl;
	    cout << s2 << endl;
	    cout << s3 << endl;
    }
    //尾插测试
    void test1()
    {
	    string s1;
	    s1.push_back('h');
	    cout << s1 << endl;
	    s1.push_back("ello");
	    cout << s1 << endl;
	    s1.append(" world");
	    cout << s1 << endl;
    }
    //插入删除测试
    void test2()
    {
    	string s1("hello world");
	    s1.insert(5, 'x');
    	cout << s1 << endl;
    	s1.insert(0, "xxxx");
	    cout << s1 << endl;

    	s1.erase(0, 4);
    	cout << s1 << endl;
    }
    //拷贝和赋值测试
    void test3()
    {
    	string s1("hello world");
	    string s2 = s1;
	    string s3;
	    s3 = s2;
	    cout << s1 << endl;
	    cout << s2 << endl;
	    cout << s3 << endl;
    }
    //查找和返回字符串测试
	void test4()
	{
		string st("hello.cpp");
		size_t fin = st.find('.');
		string st1 = st.substr(fin);
		cout << st1 << endl;
	}
}

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

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

相关文章

书生浦语三期实战营 [进阶] 茴香豆:企业级知识问答工具实践闯关任务

茴香豆&#xff1a;企业级知识问答工具实践闯关任务 1 Web 版茴香豆 1.1 创建 Web 版茴香豆账户和密码 登录 https://openxlab.org.cn/apps/detail/tpoisonooo/huixiangdou-web&#xff0c;可以看到 Web 版茴香豆的知识库注册页面&#xff0c;在对应处输入想要创建的知识库名…

Spring Cloud 搭建 Gateway 网关与统一登录模块:路径重写、登录拦截、跨域配置

文章目录 一、项目结构项目依赖 二、搭建 Gateway 服务1. 配置 Gateway2. 配置跨域 三、统一登录模块1. 配置 Spring Security2. 创建 Security 配置3. 实现认证过滤器 四、总结 博主介绍&#xff1a;全网粉丝10w、CSDN合伙人、华为云特邀云享专家&#xff0c;阿里云专家博主、…

SpringBoot教程(二十八) | SpringBoot集成Elasticsearch(Java High Level Rest Client方式)

SpringBoot教程&#xff08;二十八&#xff09; | SpringBoot集成Elasticsearch&#xff08;Java High Level Rest Client方式&#xff09; 前言添加maven依赖yml配置ElasticsearchConfig 连接配置类EsUtil 工具类开始测试 前言 由ES官方提供&#xff0c;代码语法和DSL语法相似…

UEFI——获取UEFI MemoryMap

一、MemoryMap简介 首先讲一下什么是MemoryMap&#xff1f; 内存映射&#xff08;Memory Mapping&#xff09;是一种将文件内容映射到进程的虚拟地址空间的技术。在这种机制下&#xff0c;文件可以视为内存的一部分&#xff0c;从而允许程序直接对这部分内存进行读写操作&…

电动车乱停放识别摄像头

电动车乱停放是城市交通管理中常见的问题&#xff0c;给道路通行和停车场管理带来了诸多困扰。为了有效解决这一问题&#xff0c;人们研发了电动车乱停放识别摄像头&#xff0c;这种设备结合了人工智能技术和监控摄像技术&#xff0c;能够实时监测并识别电动车乱停放情况&#…

python日常刷题(二)

前言&#xff1a;本文记录2024年4月9日至2024年4月13日做题时遇到的几个问题&#xff08;错题本&#xff09;&#xff1a; &#x1f3ac;个人简介&#xff1a;努力学习ing &#x1f4cb;本专栏&#xff1a;python日常刷题 &#x1f380;CSDN主页&#xff1a;愚润求学 文章目录 …

uniapp苹果端与安卓端兼容性问题的处理

目录 第一个问题&#xff0c;苹果端页面有下拉回弹的效果&#xff0c;安卓端没有。解决苹果端的问题&#xff0c;在pages.json中对需要的页面&#xff0c; 第二个问题&#xff0c;安卓端页面滚动到底部触发onReachBottom页面生命周期函数&#xff0c;而苹果端无法触发&#xf…

2024年PDF转换器大集合:哪4款是互联网人的首选?

嘿&#xff0c;朋友们&#xff0c;你们知道吗&#xff1f;那些在办公室里看起来特别能干的大佬们&#xff0c;他们好像总能很快地把各种文件变来变去&#xff0c;好像有什么特异功能似的。告诉你们吧&#xff0c;他们其实就是用了几款特别牛的PDF转换工具&#xff01;今天&…

前端打包装包——设置镜像

1、打包失败&#xff0c;因为没装包&#xff0c;装包失败&#xff0c;因为装包的源错误 npm config get registry npm config set registry https://registry.npmmirror.com/npm install npm run build还是失败&#xff0c;因为缺少了包&#xff0c;在package.json文件中没有包…

Centos7.9 安装Elasticsearch 8.15.1(图文教程)

本章教程,主要记录在Centos7.9 安装Elasticsearch 8.15.1的整个安装过程。 一、下载安装包 下载地址: https://www.elastic.co/cn/downloads/past-releases/elasticsearch-8-15-1 你可以通过手动下载然后上传到服务器,也可以直接使用在线下载的方式。 wget https://artifacts…

Docker 部署 Seata (图文并茂超详细)

部署 Seata ( Docker ) [Step 1] : 创建对应的**seata**数据库, 也就是下面的的**seata.sql**文件 seata.sql [Step 2] : 拉取 Seata 镜像 docker pull seataio/seata-server:1.5.2[Step 3] : 创建目录 ➡️ 创建容器 ➡️ 拷贝文件 ➡️ 授权文件 ➡️ 删除容器 # 创建…

热点文章轻松生成?一篇测评告诉你ChatGPT的神奇能力

个人名片 &#x1f393;作者简介&#xff1a;java领域优质创作者 &#x1f310;个人主页&#xff1a;码农阿豪 &#x1f4de;工作室&#xff1a;新空间代码工作室&#xff08;提供各种软件服务&#xff09; &#x1f48c;个人邮箱&#xff1a;[2435024119qq.com] &#x1f4f1…

【视频教程】遥感云大数据在灾害、水体与湿地领域典型案例实践及GPT模型应用

前期我们分别以Javascript和Python为编程语言&#xff0c;成功举办了数期关于GEE遥感大数据分析处理的基础培训课程&#xff0c;得到了来多个行业的学员的广泛参与和支持。应广大科研工作者的要求&#xff0c;本次课程将聚焦目前遥感应用最热门领域之一的灾害、水体及湿地领域&…

多线程篇(阻塞队列- LinkedTransferQueue)(持续更新迭代)

目录 一、简介 二、基本原理 三、jdk8 内部属性 4个常量值 transfer tryAppend take() awaitMatch boolean remove(Object o) 四、jdk17 主要参数 put/offer take() remove() 五、与synchronousqueue 区别 六、知识小结 一、简介 LinkedTransferQueue是一个由…

手撕Python之文件操作

1.编码 编码---encode() 解码---decode() #编码(encode):将我们能够识别的文字&#xff0c;转换为计算机能够识别的内容 print(你好.encode())#默认utf-8 #b\xe4\xbd\xa0\xe5\xa5\xbd #将你好翻成进制数#解码&#xff08;decode&#xff09;:将计算机能识别的内容&#xff0c…

兼顾身份保护和文本对齐!中山大学等提出CoRe:任意提示的文本到图像个性化生成!

文章链接&#xff1a;https://arxiv.org/pdf/2408.15914 亮点直击 CoRe只用于输出embedding和注意力图&#xff0c;不需要生成图像&#xff0c;可以用于任意提示。 在身份保护和文本对齐方面表现出优越的性能&#xff0c;特别是对于需要高度视觉可变性的提示。 除了个性化一般…

2. Python之注释及缩进以及print和input函数使用

一. Python代码中的注释及缩进 Python中注释有单行注释&#xff0c;多行注释&#xff0c;声明注释 单行注释使用符号#&#xff0c;从#开始&#xff0c;后面到本行末尾都是注释内容。多行注释可以使用’‘’ ‘’三个引号&#xff0c;三个引号内的内容都是注释声明注释&#x…

密钥加密机的定义与分类

密钥加密机&#xff0c;也称为加密机或硬件加密模块(HSM, Hardware Security Module)&#xff0c;是信息安全领域中的核心设备。它通过密码学原理和密钥管理技术&#xff0c;对敏感数据进行加密保护&#xff0c;确保数据在传输和存储过程中的安全性。以下是对密钥加密机的详细解…

开始一个WPF项目时的记忆重载入

目前在工业软件的UI开发方案选择中&#xff0c;WPF仍然是一个重要的选项。 但是其固有的复杂性&#xff0c;对于像我这样&#xff0c;并不是一直在从事界面开发的人来说&#xff0c;每次重启&#xff0c;都需要一两天的适应的时间。所以这里稍微写一个笔记。 还是老办法&…

分享一款520表白节JS代码

源码介绍 今天给大家分享一款JS表白源码 js会随 随机颜色心形跟随鼠标互动520表白节女神表白利器&#xff01; 修改的话就搜索&#xff1a;LOVEh 就能找到这个英文了。 效果预览 源码获取 分享一款520表白节JS代码 百度网盘提取码&#xff1a;2yar&#xff1a;https://pan.…