C++——string的模拟实现(下)

目录

成员函数

3.4 修改操作

(3)insert()函数

(4)pop_back()函数

(5)erase()函数

(6)swap()函数

3.5 查找操作

(1)find()函数

 (2)substr()函数

3.6 重载函数

(1)operator=赋值函数

(2)其他比较函数

(3)流插入和流提取

完整代码

结束语


第一篇链接:C++——string的模拟实现(上)

成员函数

3.4 修改操作
(3)insert()函数

insert() 函数用于在字符串的指定位置插入一个字符或字符串。

注意事项:

在移动字符串内容时,需要确保不会覆盖或丢失任何字符。

使用 memcpy() 时,要确保源和目标内存区域不重叠,否则可能导致未定义行为。

string.h:

	// insert()函数 
	void insert(size_t pos, char ch);	//插入字符
	void insert(size_t pos, const char* str);	//插入字符串

string.cpp:

// insert()函数
// 插入字符
void string::insert(size_t pos, char ch)
{
	assert(pos <= _size);
	if (_capacity == _size)
	{
		size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;
		reserve(newcapacity);
	}
	size_t end = _size + 1;
	// 挪动元素
	while (pos < end)
	{
		_str[end] = _str[end - 1];
		end--;
	}
	_str[pos] = ch;		//将ch放置到pos这个位置
	++_size;			//更新_size
}

// 插入字符串
void string::insert(size_t pos, const char* str)
{
	assert(pos <= _size);
	size_t len = strlen(str);	// 要插入的字符串长度
	if (_size + len > _capacity)
	{
		reserve(_size + len);
	}
	// 从后向前移动字符串内容以腾出空间  
	for (size_t i = _size; i >= pos; --i)
	{
		_str[i + len] = _str[i];
	}

	// 复制新字符串到指定位置  
	memcpy(_str + pos, str, len);

	// 添加新的字符串结束符  
	_str[_size + len] = '\0';

	// 更新字符串长度  
	_size += len;
}

来个简单的代码测试一下:

(4)pop_back()函数

pop_back() 函数用于移除字符串末尾的字符。

实现:将字符串末尾的字符设置为 '\0'。 更新字符串长度 _size。

string.h:

// pop_back()函数
void pop_back();

string.cpp:

// pop_back()函数
void string::pop_back()
{
	_str[_size - 1] = '\0';
	--_size;
}
(5)erase()函数

erase() 函数用于从字符串中移除指定位置的字符或子字符串。

string.h:

// erase()函数
void erase(size_t pos = 0, size_t len = npos);

string.cpp:

// erase()函数
void string::erase(size_t pos,size_t len)
{
	assert(pos < _size);				 

	if (len == npos || len >= _size - pos)
	{
		_str[pos] = '\0';		// 位置pos置为'\0'
		_size = pos;			// 有效元素个数为pos个
	}
	else	// len小于后面的字符个数
	{
		// 将后面的字符拷贝到pos位置
		strcpy(_str + pos, _str + pos + len);
		_size -= len;			// 更新有效元素
	}
}

erase()需要额外定义一个类成员变量npos来实现,它为无符号数的-1,一般为整型的最大值。

string.h:

public:
	static const size_t npos;

string.cpp:

const size_t string::npos = -1;

简单的对上面两个函数进行测试:

(6)swap()函数

swap() 函数用于交换两个字符串的内容。

string.h:

// swap()函数
void swap(string& str);

string.cpp:

// swap()函数
void string::swap(string& str)
{
	// 这里我们调用std库中的swap()函数
	std::swap(_str, str._str);
	std::swap(_capacity, str._capacity);
	std::swap(_size, str._size);
}

测试:

3.5 查找操作
(1)find()函数

find() 函数用于在字符串中查找指定字符或子字符串的位置。

我们在这里实现从指定位置开始向后遍历查找指定字符或者子字符串。

如果找到,则返回子字符串在字符串中的起始位置;否则返回 npos。

string.h:

// find()函数
size_t find(char ch, size_t pos);
size_t find(const char* str, size_t pos);

string.cpp:

// find()函数
// 寻找字符
size_t string::find(char ch, size_t pos)
{
	for (int i = pos; i < _size; i++)
	{
		if (_str[i] == ch)
		{
			return i;
		}
	}
	return npos;
}

// 从pos开始寻找字符串
size_t string::find(const char* str, size_t pos)
{
	char* p = strstr(_str + pos, str);
	if (p)
	{
		return p - _str;
	}
	else
	{
		return npos;
	}
}
 (2)substr()函数

substr() 函数用于获取字符串的子字符串。

string.h:

// substr()函数
string substr(size_t pos = 0, size_t len = npos);

string.cpp:

// substr()函数
string string::substr(size_t pos, size_t len)
{
	string str;
	if (len == npos || len >= _size - pos)
	{
		for (size_t i = pos; i < _size; i++)
		{
			str += _str[i];
		}
	}
	else
	{
		for (size_t i = pos; i < pos + len; i++)
		{
			str += _str[i];
		}
	}
	return str;
}
3.6 重载函数

在C++中,函数重载允许我们为同一函数名创建多个版本,这些版本可以有不同的参数列表(参数的数量或类型不同)。通过这种方式,我们可以使代码更加简洁和易于理解。

(1)operator=赋值函数

赋值运算符(operator=)用于将一个对象的内容复制到另一个对象中。对于自定义的字符串类,我们需要自己实现这个运算符,以确保正确地管理内存。

string.h:

// operator=赋值函数
string& operator=(const string& s);

string.cpp:

// operator= 函数
string& string::operator=(const string& str)
{
	if (this != &str)
	{
		char* tmp = new char[str._capacity + 1];
		strcpy(tmp, str._str);
		delete[] _str;

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

来测试一下:

这里我们可以对以上的代码进行优化:

// operator= 函数
string& string::operator=(const string& str)
{
	if (this != &str)
	{
		string tmp(str._str);	// 调用构造函数
		swap(tmp);				// 将tmp与this交换
	}
	return *this;
}

原始实现中,我们首先检查自赋值(即对象试图将自己赋值给自己),然后分配足够的内存来存储新字符串,复制内容,并释放旧内存。这种方法虽然有效,但可能会导致性能问题,特别是当字符串很长时,因为涉及到多次内存分配和释放。

优化后的实现采用了“拷贝-交换”技术。这种方法通过创建一个临时对象来存储要赋值的字符串,然后使用swap函数交换临时对象和当前对象的内容。由于swap函数通常实现得非常高效(只需交换指针),这种方法可以显著提高性能,并减少内存分配的次数。

(2)其他比较函数

每个比较运算符都基于strcmp函数的返回值来实现。

这些函数之间是可以复用的。

string.h:

// 比较函数
bool operator<(const string& s)const;
bool operator<=(const string& s)const;
bool operator>(const string& s)const;
bool operator>=(const string& s)const;
bool operator==(const string& s)const;
bool operator!=(const string& s)const;

string.cpp:

// 比较函数
bool string::operator<(const string& str) const
{
	return strcmp(_str, str._str) < 0;
}
bool string::operator<=(const string& str) const
{
	return *this < str || *this == str;
}
bool string::operator>(const string& str) const
{
	return !(*this <= 0);
}
bool string::operator>=(const string& str) const
{
	return !(*this <= str);
}
bool string::operator==(const string& str)const
{
	return strcmp(_str, str._str) == 0;
}
bool string::operator!=(const string& str)const
{
	return !(*this == str);
}

void string::clear()
{
	_str[0] = '\0';
	_size = 0;
}
(3)流插入和流提取

我们接下来试着实现流插入和流提取:

注意,我们不能将这两个函数定义放在string类里

在 C++ 中,当我们使用 std::cout << d1 或 std::cin >> d1 这样的表达式时,左侧的 std::cout 或 std::cin 是对象,而右侧的 d1 是我们想要输出或输入的数据。运算符函数需要能够接受这两个对象作为参数。 如果 << 或 >> 运算符被定义为 std::string 的成员函数,那么它们将需要额外的 this 指针来访问类的成员变量,这将导致需要三个参数(this 指针,左操作数,右操作数),这与标准的运算符用法不兼容。

string.h:

//流插入和流提取
istream& operator>>(istream& is, string& str);
ostream& operator<<(ostream& os, const string& str);

string.cpp:

// 流输入
istream& operator>>(istream& is, string& str)
{
	// 清除字符串 str 中的现有内容  
	str.clear();
	char ch;
	// 从输入流 is 中读取第一个字符到 ch 中  
	ch = is.get();

	// 定义一个字符数组(缓冲区)buff
	char buff[128];
	size_t i = 0;
	// 循环读取字符,直到遇到空格或换行符为止  
	while (ch != ' ' && ch != '\n')
	{
		// 将字符 ch 存储到缓冲区 buff 的当前位置  
		buff[i++] = ch;  
		if (i == 127)
		{
			// 在缓冲区末尾添加字符串结束符 '\0'
			buff[i++] = '\0';
			str += buff;

			// 重置索引 i,为下一轮存储字符做准备  
			i = 0;
		}
		// 从输入流中读取下一个字符到 ch 中  
		ch = is.get();
	}

	// 循环结束后,检查缓冲区中是否还有未处理的字符  
	if (i > 0)
	{
		// 在缓冲区末尾添加字符串结束符 '\0'  
		buff[i] = '\0';

		// 将缓冲区的内容追加到字符串 str 中  
		str += buff;
	}
	return is;
}
// 流提取
ostream& operator<<(ostream& os, const string& str)
{
	for (size_t i = 0; i < str.size(); i++)
	{
		os << str[i];
	}
	return os;
}

测试一下:

完整代码

string.h:

#include<iostream>
#include<assert.h>
#include<stdbool.h>
using namespace std;

namespace My_string
{
	class string
	{
	public:
		typedef char* iterator;			//将char*重命名为iterator
		typedef const char* const_iterator;

		//const版本的iterator
		const_iterator begin() const;	//提供const_iterator begin()函数
		const_iterator end() const;		//提供const_iterator end()函数

		//非const版本的iterator
		iterator begin();				//提供iterator begin()函数
		iterator end();					//提供iterator end()函数

		string(const char* str = " ");	//构造函数
		~string();						//析构函数
		string(const string& str);		//拷贝构造函数

		const char* c_str() const;		// c_str()函数
		size_t size() const;			// size()函数
		size_t capacity() const;		// capacity()函数
		bool empty() const;				// empty()函数
		
		// resize()函数
		void resize(size_t n, char ch = '\0');

		// 非const版本
		char& operator[](size_t pos);	//operator[]函数
		// const版本
		const char& operator[](size_t pos)const;

		// 预留空间
		void reserve(size_t n);
		// 尾插一个字符
		void push_back(char ch);
		// 尾插一个字符串
		void append(const char* str);
		
		//operator+=函数可以构成重载,函数名相同,参数不同
		string& operator+=(char ch);			// 字符相加
		string& operator +=(const char* str);	// 字符串相加

		// insert()函数 
		void insert(size_t pos, char ch);	//插入字符
		void insert(size_t pos, const char* str);	//插入字符串

		// erase()函数
		void erase(size_t pos = 0, size_t len = npos);

		// pop_back()函数
		void pop_back();

		// find()函数
		size_t find(char ch, size_t pos);
		size_t find(const char* str, size_t pos);

		// substr()函数
		string substr(size_t pos = 0, size_t len = npos);

		// operator=赋值函数
		string& operator=(const string& s);

		// swap()函数
		void swap(string& str);

		// 比较函数
		bool operator<(const string& s)const;
		bool operator<=(const string& s)const;
		bool operator>(const string& s)const;
		bool operator>=(const string& s)const;
		bool operator==(const string& s)const;
		bool operator!=(const string& s)const;

		// clear()函数
		void clear();

	public:
		static const size_t npos;
	private:
		char* _str;			// 指向字符串的指针
		size_t _size;		// 有效字符个数
		size_t _capacity;	// 有效空间个数
	};

	//流插入和流提取
	istream& operator>>(istream& is, string& str);
	ostream& operator<<(ostream& os, const string& str);
}

string.cpp:

#include"string.h"
namespace My_string 
{
	const size_t string::npos = -1;

	string::iterator string::begin()
	{
		return _str;
	}
	string::iterator string::end()
	{
		return _str + _size;
	}
	string::const_iterator string::begin() const
	{
		return _str;
	}
	string::const_iterator string::end() const
	{
		return _str + _size;
	}

	// 构造函数
	string::string(const char* str)
	{
		_size = strlen(str);
		_capacity = _size;
		_str = new char[_capacity + 1];		// +1用于储存'\0'
		strcpy(_str, str);
	}

	// 析构函数
	string::~string()
	{
		delete[] _str;
		_str = nullptr;
		_size = _capacity = 0;
	}

	// 拷贝构造函数(1)
	string::string(const string& str)
	{
		_str = new char[str._capacity + 1];	//额外多给一个空间,用于存放'/0'
		strcpy(_str, str._str);		//拷贝数据
		_capacity = str._capacity;	//设置容量
		_size = str._size;			//设置有效数据个数
	}

	// 拷贝构造函数(2)
	//string::string(const string& str)
	//{
	//	string tmp(str._str);
	//	std::swap(tmp._str, _str);
	//	std::swap(tmp._size, _size);
	//	std::swap(tmp._capacity, _capacity);
	//}

	// 拷贝构造函数(3)
	//string::string(const string& str)
	//{
	//	string tmp(str._str);
	//	swap(tmp);			// 这里的swap我们接下来会定义
	//}

	// c_str()函数
	const char* string::c_str() const
	{
		return _str;
	}

	// size()函数
	size_t string::size() const
	{
		return _size;
	}

	// capacity()函数
	size_t string::capacity() const
	{
		return _capacity;
	}

	// empty()函数
	bool string::empty() const
	{
		return _size == 0;
	}

	// operator[]函数
	char& string::operator[](size_t pos)
	{
		assert(pos < _size);
		return _str[pos];
	}
	// const版本
	const char& string::operator[](size_t pos)const
	{
		assert(pos < _size);
		return _str[pos];
	}

	// 预留空间
	void string::reserve(size_t n)
	{
		if (n > _capacity)
		{
			char* tmp = new char[n + 1];
			strcpy(tmp, _str);
			delete[] _str;
			_str = tmp;
			_capacity = n;
		}
	}

	// resize()函数
	void string::resize(size_t n, char ch)
	{
		if (n > _size)
		{
			if (n > _capacity)
			{
				reserve(n);
			}
			// 使用 memset 函数将字符 ch 
			// 填充到新添加的空间中
			memset(_str + _size, ch, n - _size);
		}
		_size = n;
		_str[n] = '\0';
	}

	//尾插一个字符
	void string::push_back(char ch)
	{
		if (_capacity == _size)
		{
			size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;
			reserve(newcapacity);
		}
		_str[_size] = ch;
		_str[_size + 1] = '\0';
		_size++;
	}

	//尾插一个字符串
	void string::append(const char* str)
	{
		size_t len = strlen(str);

		if (_size + len > _capacity) 
		{
			reserve(_size + len);
		}
		strcpy(_str+_size, str);
		_size += len;			
	}

	string& string::operator+=(char ch)
	{
		// 调用push_back()函数
		push_back(ch);		
		return *this;
	}
	string& string::operator+=(const char* str)
	{
		append(str);
		return *this;
	}

	// insert()函数
	// 插入字符
	void string::insert(size_t pos, char ch)
	{
		assert(pos <= _size);
		if (_capacity == _size)
		{
			size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;
			reserve(newcapacity);
		}
		size_t end = _size + 1;
		// 挪动元素
		while (pos < end)
		{
			_str[end] = _str[end - 1];
			end--;
		}
		_str[pos] = ch;		//将ch放置到pos这个位置
		++_size;			//更新_size
	}

	// 插入字符串
	void string::insert(size_t pos, const char* str)
	{
		assert(pos <= _size);
		size_t len = strlen(str);	// 要插入的字符串长度
		if (_size + len > _capacity)
		{
			reserve(_size + len);
		}
		// 从后向前移动字符串内容以腾出空间  
		for (size_t i = _size; i >= pos; --i)
		{
			_str[i + len] = _str[i];
		}

		// 复制新字符串到指定位置  
		memcpy(_str + pos, str, len);

		// 添加新的字符串结束符  
		_str[_size + len] = '\0';

		// 更新字符串长度  
		_size += len;
	}

	// erase()函数
	void string::erase(size_t pos,size_t len)
	{
		assert(pos < _size);				 

		if (len == npos || len >= _size - pos)
		{
			_str[pos] = '\0';		// 位置pos置为'\0'
			_size = pos;			// 有效元素个数为pos个
		}
		else	// len小于后面的字符个数
		{
			// 将后面的字符拷贝到pos位置
			strcpy(_str + pos, _str + pos + len);
			_size -= len;			// 更新有效元素
		}
	}

	// pop_back()函数
	void string::pop_back()
	{
		_str[_size - 1] = '\0';
		--_size;
	}

	// find()函数
	// 寻找字符
	size_t string::find(char ch, size_t pos)
	{
		for (int i = pos; i < _size; i++)
		{
			if (_str[i] == ch)
			{
				return i;
			}
		}
		return npos;
	}

	// 从pos开始寻找字符串
	size_t string::find(const char* str, size_t pos)
	{
		char* p = strstr(_str + pos, str);
		if (p)
		{
			return p - _str;
		}
		else
		{
			return npos;
		}
	}

	// substr()函数
	string string::substr(size_t pos, size_t len)
	{
		string str;
		if (len == npos || len >= _size - pos)
		{
			for (size_t i = pos; i < _size; i++)
			{
				str += _str[i];
			}
		}
		else
		{
			for (size_t i = pos; i < pos + len; i++)
			{
				str += _str[i];
			}
		}
		return str;
	}

	// operator= 函数
	//string& string::operator=(const string& str)
	//{
	//	if (this != &str)
	//	{
	//		char* tmp = new char[str._capacity + 1];
	//		strcpy(tmp, str._str);
	//		delete[] _str;

	//		_str = tmp;
	//		_size = str._size;
	//		_capacity = str._capacity;
	//	}
	//	return *this;
	//}

	// operator= 函数
	string& string::operator=(const string& str)
	{
		if (this != &str)
		{
			string tmp(str._str);	// 调用构造函数
			swap(tmp);				// 将tmp与this交换
		}
		return *this;
	}

	// swap()函数
	void string::swap(string& str)
	{
		// 这里我们调用std库中的swap()函数
		std::swap(_str, str._str);
		std::swap(_capacity, str._capacity);
		std::swap(_size, str._size);
	}

	// 比较函数
	bool string::operator<(const string& str) const
	{
		return strcmp(_str, str._str) < 0;
	}
	bool string::operator<=(const string& str) const
	{
		return *this < str || *this == str;
	}
	bool string::operator>(const string& str) const
	{
		return !(*this <= 0);
	}
	bool string::operator>=(const string& str) const
	{
		return !(*this <= str);
	}
	bool string::operator==(const string& str)const
	{
		return strcmp(_str, str._str) == 0;
	}
	bool string::operator!=(const string& str)const
	{
		return !(*this == str);
	}

	void string::clear()
	{
		_str[0] = '\0';
		_size = 0;
	}

	// 流输入
	istream& operator>>(istream& is, string& str)
	{
		// 清除字符串 str 中的现有内容  
		str.clear();
		char ch;
		// 从输入流 is 中读取第一个字符到 ch 中  
		ch = is.get();

		// 定义一个字符数组(缓冲区)buff
		char buff[128];
		size_t i = 0;
		// 循环读取字符,直到遇到空格或换行符为止  
		while (ch != ' ' && ch != '\n')
		{
			// 将字符 ch 存储到缓冲区 buff 的当前位置  
			buff[i++] = ch;  
			if (i == 127)
			{
				// 在缓冲区末尾添加字符串结束符 '\0'
				buff[i++] = '\0';
				str += buff;

				// 重置索引 i,为下一轮存储字符做准备  
				i = 0;
			}
			// 从输入流中读取下一个字符到 ch 中  
			ch = is.get();
		}

		// 循环结束后,检查缓冲区中是否还有未处理的字符  
		if (i > 0)
		{
			// 在缓冲区末尾添加字符串结束符 '\0'  
			buff[i] = '\0';

			// 将缓冲区的内容追加到字符串 str 中  
			str += buff;
		}
		return is;
	}

	// 流提取
	ostream& operator<<(ostream& os, const string& str)
	{
		for (size_t i = 0; i < str.size(); i++)
		{
			os << str[i];
		}
		return os;
	}
}

test.cpp:

#include"string.h"
#include<iostream>

using namespace std;

void test1()
{
	// 测试构造函数  
	My_string::string s1("Hello");
	cout << "s1: " << s1.c_str() << endl;

	// 测试拷贝构造函数  
	/*My_string::string s2(s1);
	cout << "s2 (s1): " << s2.c_str() << endl;	*/
}

void test2()
{
	My_string::string str("Hello");
	str[0] = 'h';
	cout << str.c_str() << endl;

	//使用operator[]函数打印
	for (size_t i = 0; i < str.size(); i++)
	{
		cout << str[i] << " ";
	}
	cout << endl;
}

void test3()
{
	My_string::string str("hello");

	for (auto i : str)
	{
		cout << i << " ";
	}
	cout << endl;

	My_string::string::iterator it1 = str.begin();
	while (it1 != str.end())
	{
		cout << *it1 << " ";
		++it1;
	}
	cout << endl;

	My_string::string::iterator it2 = str.end();
	if (it2 != str.begin()) 
	{ // 检查避免直接解引用 end()  
		--it2; // 先移动到一个有效的位置  
		while (it2 != str.begin())
		{
			std::cout << *it2 << " ";
			--it2;
		}
		std::cout << *it2 << " "; // 输出最后一个字符(begin() 之前的字符)  
	}
	std::cout << std::endl;
}

void test4()
{
	My_string::string str("hello");
	cout << str.c_str() << endl;

	str.push_back('A');
	cout << str.c_str() << endl;

	str.append("world");
	cout << str.c_str() << endl;

	str += 'C'; 
	cout << str.c_str() << endl;
}

void test5()
{
	My_string::string str("hello world");
	
	str.insert(6, 'x');
	cout << str.c_str() << endl;
	str.insert(6, "yyyyy");
	cout << str.c_str() << endl;
	str.insert(0, 'X');
	cout << str.c_str() << endl;
}


void test6()
{
	My_string::string str("hello world");

	str.pop_back();
	cout << str.c_str() << endl;

	str.erase(2, 3);
	cout << str.c_str() << endl;
}

void test7()
{
	My_string::string str("hello world");

	cout << str.find('l', 0) << endl;

	cout << str.find("ld", 0) << endl;
}

void test8()
{
	My_string::string str1("hello");
	My_string::string str2("world");
	cout << str1.c_str() << endl;
	cout << str2.c_str() << endl;

	str1.swap(str2);
	cout << str1.c_str() << endl;
	cout << str2.c_str() << endl;
}

void test9()
{
	My_string::string str("hello World");
	cout << str.substr(2, 2) << endl;
	cout << str.substr(7, 3) << endl;
}

void test10()
{
	My_string::string str1("hello world");
	My_string::string str2("xxxxxxxxxxx");
	str1 = str2;
	cout << str1.c_str() << endl;
	cout << str2.c_str() << endl;

	My_string::string str3("yyyyy");
	str1 = str3;
	cout << str1.c_str() << endl;
	cout << str3.c_str() << endl;
}

void test11()
{
	My_string::string str("hello");
	str.resize(10, '*');
	cout << str.c_str() << endl;

	str.reserve(15);
	cout << str.c_str() << endl;
}

void test12()
{
	My_string::string str("XXX");
	cin >> str;
	cout << str << endl;
}

int main()
{
	//test1();
	//test2();
	//test3();
	//test4();
	//test5();
	//test6();
	//test7();
	//test8();
	//test9();
	//test10();
	//test11();
	test12();
	return 0;
}

结束语

实验周太忙啦!!!一直没时间写。。。

感谢各位大佬的阅读!!!

求点赞收藏评论关注!!!

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

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

相关文章

基于Springboot无人驾驶车辆路径规划系统(源码+定制+开发)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

【大模型】Ollama+WebUI+AnythingLLM搭建本地知识库

目录 1 部署Ollama 1.1 下载镜像ollama 1.2 运行ollama 1.3 验证 2 模型选型 3 安装模型 3.1 选择模型安装 3.2 删除模型(选看) 4 安装webUI 4.1 拉镜像 4.2 启动服务 5 访问 5.1 注册 5.2 登录 5.3 设置 6 使用 7 使用api来调用 8 安装AnythingLLM搭建本地…

27.9 调用go-ansible执行playbook拷贝json文件重载采集器

本节重点介绍 : go-ansible执行playbook编写分发重载的playbook编译执行 测试停掉一个节点测试停掉的节点再回来 go-ansible执行playbook 新增 goansiblerun/run.go package goansiblerunimport ("context""github.com/apenella/go-ansible/pkg/execute&qu…

Python基础学习(四)程序控制结构

代码获取&#xff1a;https://github.com/qingxuly/hsp_python_course 完结版&#xff1a;Python基础学习完结版 程序控制结构 程序流程控制介绍 基本介绍 程序流程控制绝对程序是如何执行的&#xff0c;是我们必须掌握的&#xff0c;主要有三大流程控制语句。顺序控制、分支…

Linux中DHCP服务器配置和管理

文章目录 一、DHCP服务1.1、DHCP的工作流程1.2、DHCP的工作模式1.3、dhcp的主要配置文件 二、安装DHCP服务2.1、更新yum源2.2、安装DHCP服务软件包2.3、配置DHCP服务2.4、启用DHCP服务&#xff08;解决报错&#xff09;2.4.1、查看dhcpd服务的状态和最近的日志条目2.4.2、查看与…

js构造函数和原型对象,ES6中的class,四种继承方式

一、构造函数 1.构造函数是一种特殊的函数&#xff0c;主要用来初始化对象 2.使用场景 常见的{...}语法允许创建一个对象。可以通过构造函数来快速创建多个类似的对象。 const Peppa {name: 佩奇,age: 6,sex: 女}const George {name: 乔治,age: 3,sex: 男}const Mum {nam…

【react 和 vue】 ---- 实现组件的递归渲染

1. 需求场景 今天遇到了一个需求&#xff0c;就是 HTML 的递归渲染。问题就是商品的可用时间&#xff0c;使用规则等数据是后端配置&#xff0c;然后配置规则则是可以无限递归的往下配置&#xff0c;可以存在很多级。后端实现后&#xff0c;数据返回前端&#xff0c;就需要前端…

【mysql 进阶】2-1. MySQL 服务器介绍

MySQL 服务器简介 通常所说的 MySQL 服务器指的是mysqld程序&#xff0c;当运⾏mysqld后对外提供MySQL 服务&#xff0c;这个专题的内容涵盖了以下关于MySQL 服务器以及相关配置的内容&#xff0c;包括&#xff1a; 服务器⽀持的启动选项。可以在命令⾏和配置⽂件中指定这些选…

前后端请求、返回数据的多种方式

Springboot项目的业务逻辑 &#x1f319;项目基本结构&#xff1a; 通常情况下&#xff0c;我们在搭建后端项目的时候&#xff0c;处理业务逻辑我们需要用到Controller,Service,Mapper(mybatis,mybatis-plus)&#xff0c;Entry各层之间的相互调用来完成&#xff0c;还有就是我…

数据库->增删改查

目录 一、CRUD简介 二、Create新增 1.单行数据插入 2.查询 3. 多行数据插入 4.执行本机的SQL脚本插入 三、Retrieve检索 1.全列查询 2.指定列查询 3.查询字段为表达式 3.1 常量表达式 3.2列的值与常量运算 3.3列与列之间的运算 3.4为列指定别名 4.结果查询去重…

高等数学-宋浩版2.0-映射

映射&#xff1a;X,Y为非空集合&#xff0c;存在法则F,对X(原像)中每个元素X&#xff0c;按法则F&#xff0c;在Y中有唯一元素与之对应&#xff0c;F为x到Y&#xff08;镜像&#xff09;的映射。f:X->Y X原像&#xff0c;Y像&#xff0c;x定义域&#xff0c;Df,Rf &#x…

php后端学习,Java转php

遇到前后端跨域 php解决跨域问题可以加上下面的代码&#xff1a; header(“Access-Control-Allow-Origin:*”); 并且查看自己的数据库信息是否连接成功。 从Java转php 个人感受php跟偏向前端&#xff0c; 写后端逻辑时没有像java又springboot工具方便。 但是和前端联调很方便…

BUUCTF之web篇

第一题 [极客大挑战 2019]EasySQL 打开靶机后可以看到这是一个登陆的页面 我们可以尝试两种方式登录 弱口令爆破&#xff08;burpsuite&#xff09; 通过SQL注入里的万能密码来跳过账户和密码验证的过程 这里就需要万能密码aor true # 在这里单引号的作用是结束用户名或者密码…

Python基于TensorFlow实现简单循环神经网络回归模型(SimpleRNN回归算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后关注获取。 1.项目背景 Simple RNN是一种基础的循环神经网络&#xff0c;它能够处理序列数据&#xff0c;例如文本、时间序…

React写关键字高亮的三个方案

1.js正则replaceAlldangerouslySetInnerHTML{{ __html: xxx }}危险属性 步骤最简单,但是是危险属性,不推荐使用,项目中实在没有头绪,可以使用它应急 通过useMemo计算得到新的状态值,赋值给dangerouslySetInnerHTML属性的__html 关键代码: const [state1, setState1] useSt…

【网络原理】网络地址转换----NAT技术详解

&#x1f490;个人主页&#xff1a;初晴~ &#x1f4da;相关专栏&#xff1a;计算机网络那些事 我们在 IP协议 一文中介绍过&#xff0c;由于IPv4协议中 IP地址只有32位&#xff0c;导致最多只能表示 42亿9千万个IP地址。但我们需要通过IP地址来标识网络上的每一个设备&#x…

java核心技术点都有哪些

1. 面向对象编程&#xff08;OOP&#xff09; 核心概念&#xff1a;类、对象、继承、封装、多态。 比喻&#xff1a;面向对象编程就像是在搭建一个积木城堡。类&#xff08;Class&#xff09;是城堡的设计图纸&#xff0c;它定义了城堡的结构和功能&#xff1b;对象&#xff08…

传输层TCP

报头 1.报头和有效载荷如何分离将&#xff0c;有效载荷向上交付&#xff1f; tcp有个标准报头长度为20&#xff0c;那是不是以为我们可以像udp一样分离依靠报头大小去分离&#xff0c;我们仔细去看我们报头中还有个选项没包含到。 我们还有个首部长度&#xff0c;四位可以表…

测试代理IP的有效性和可用性

使用代理IP的有效性和可用性直接关系到用户的工作效率&#xff0c;尤其是在进行数据抓取、网络爬虫和保护个人隐私等场景中。 一、测试代理IP的必要性 代理IP的可用性测试是确保代理服务正常运行的重要步骤。测试代理IP的必要性主要体现在以下几个方面&#xff1a; 提升工作…

【Docker命令】日常使用的Docker命令

Docker常用命令 1、基础命令2、容器管理3、镜像管理推送镜像 4、网络管理5、数据管理 1、基础命令 - docker run&#xff1a;运行一个容器,--name 指定容器的名称&#xff0c;-i 获取标准输入输出&#xff0c;-t显示在终端&#xff0c;-d放到后台运行&#xff0c;--rm容器停止…