string类函数的手动实现

在上一篇文章中,我们讲解了一些string类的函数,但是对于我们要熟练掌握c++是远远不够的,今天,我将手动实现一下这些函数~

注意:本篇文章中会大量应用复用,这是一种很巧妙的方法

和以往一样,还是分为string.h string.cpp test.cpp三个文件

为了保证完整性,string.h我统一放在这

1.string.h文件

#pragma once
#include<iostream>
#include<assert.h>
#include<string.h>
using namespace std;
namespace my_string
{
	class string
	{
	public:
		typedef char* iterator;
		typedef const char* const_iterator;

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

		const_iterator end() const
		{
			return _str + _size;
		}

		string(const char* str = "");
		string(size_t n, char ch);
		string(const string& s);
		string& operator=(const string& s);
		~string();
		void clear()
		{
			_str[0] = '\0';
			_size = 0;
		}
		const char* c_str() const
		{
			return _str;
		}
		void reserve(size_t n);
		void push_back(char ch);
		void append(const char* str);
		string& operator+=(char ch);
		string& operator+=(const char* str);
		void insert(size_t pos, size_t n, char ch);
		void insert(size_t pos, const char* str);
		void erase(size_t pos = 0, size_t len = npos);
		size_t find(char ch, size_t pos = 0);
		size_t find(const char* str, size_t pos = 0);
		size_t size()const
		{
			return _size;
		}
		size_t capacity()const
		{
			return _size;
		}
		char& operator[](size_t pos) const
		{
			assert(pos < _size);
			return _str[pos];
		}
		string substr(size_t pos, size_t len = npos);
		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;
	private:
		char* _str;
		size_t _size;
		size_t _capacity;
		const static size_t npos;
	};
	//cout<<s1
	ostream& operator<<(ostream& out, const string& s);
	//cin>>s1
	istream& operator<<(istream& in, string& s);
	istream& getline(istream& is, string& s, char delim = '#');

}

2. 增加类函数(append\insert\push_back\+=)

这是string.cpp文件

	void string::push_back(char ch)
	{
		if (_size + 1 > _capacity)
		{
			//意味着此时已经满了,需要扩容才能插入
			//扩容,建议使用函数复用
			//还要讨论原来容量是不是0
			reserve(_capacity == 0 ? 4 : _capacity * 2);
		}
		//此时已经完成扩容,容量足够用
		_str[_size] = ch;
		_size++;
		_str[_size] = '\0';            //别忘了把\0也考过来
	}
	void string::append(const char* str)
	{
		//注:我们这里是直接按照库里的思路去实现的  
		// 在这里扩容_size+len也是可以的   只不过思路不一样
		// 也可能官方认为追加直接扩二倍  后面人继续使用的时候可以少调几次开空间吧
		size_t len = strlen(str);
		if (_size + len > _capacity)
		{
			//意味着此时已经满了,需要扩容才能插入
			//扩容,与push_back不同的是,
			// 默认使用append是认为你这个字符串原先就是有内容才追加的
			// 如果害怕有人确实会直接使用这个接口 可以加上_capacity=0的情况
			size_t newcapacity = 2 * _capacity;
			//考虑到可能插入的字符串过长,2倍扩容都可能不够
			//为防止越界的产生,我们再严谨的讨论一下
			if (_size + len > 2 * _capacity)
			{
				newcapacity = _size + len;
			}
			reserve(newcapacity);
		}
		//strcpy在拷贝时会从第一个字符出发找\0,
		// 为了节约编译器运行时间,我们直接手动让他从\0出发
		strcpy(_str + _size, str);   
		_size += len; 
	}
	void string::insert(size_t pos, size_t n, char ch)
	{
		assert(pos <= _size);
		assert(n > 0);
		//还是要考虑扩容问题
		if (_size + n > _capacity)
		{
			size_t newcapacity = 2 * _capacity;
			if (_size + n > 2 * _capacity)
			{
				newcapacity = _size + n;
			}
			reserve(newcapacity);
		}
		size_t end = _size + n;       //一切以\0为准
		while (end > pos + n - 1)     //准备挪动数据,这是需要挪动的数据范围
		{
			_str[end] = _str[end - n];//最后一个数据先动
			end--;
		}
		//挪完了,有地方了,但是要插入的数还没进来呢!
		for (size_t i = 0; i < n; i++)
		{
			_str[pos + i] = ch;
		}
		_size += n;
	}
	void string::insert(size_t pos, const char* str)
	{
		assert(pos <= _size);
		size_t n = strlen(str);
		if (_size + n > _capacity)
		{
			size_t newCapacity = 2 * _capacity;
			if (_size + n > 2 * _capacity)
			{
			    newCapacity = _size + n;
			}
			reserve(newCapacity);
		}
		size_t end = _size + n;
        while (end > pos + n - 1)
        {
	        _str[end] = _str[end - n];
	        --end;
        }
		for (size_t i = 0; i < n; i++)
		{
			_str[pos + i] = str[i];
		}
	}
string& string::operator+=(char ch)
{
	push_back(ch);
	return *this;
}
string& string::operator+=(const char* str)
{
	append(str);
	return *this;
}

这是test.cpp文件

#include"string.h"
void test_string1()
{
	string s1("hello world");
	cout << s1.c_str() << endl;
	s1 += ' ';
	cout << s1.c_str() << endl;
	s1 += '+';
	cout << s1.c_str() << endl;
	s1 += "hello everybody";
	cout << s1.c_str() << endl;
	s1.push_back(',');
	cout << s1.c_str() << endl;
	s1.append("welcome!");
	cout << s1.c_str() << endl;
	s1.insert(6,1, 't');
	cout << s1.c_str() << endl;
	s1.insert(7, "he ");
	cout << s1.c_str() << endl;
	s1.insert(41, "nice to meet you");
	cout << s1.c_str() << endl;
	s1.insert(0, "good morning!");
	cout << s1.c_str() << endl;
}
int main()
{
	test_string1();
	return 0;
}

 结果如下:

2.find和erase

这是string.cpp文件

	size_t string::find(char ch, size_t pos)
	{
		for (size_t i = pos; i < _size; i++)
		{
			if (_str[i] == ch)
			{
				return i;
			}
		}
		return npos;
	}
	size_t string::find(const char* str, size_t pos)
	{
		const char* p = strstr(_str + pos, str);   
		//strstr() 函数的作用是在一个字符串(str1)中查找另一个字符串(str2)的出现位置。
		//如果找到,它返回一个指向 str1 中第一次出现的 str2 的指针;
		// 如果找不到,则返回空指针(NULL)。
		if (p == nullptr)
		{
			return npos;
		}
		else
		{
			return p - _str;      //两个指针相减,结果得到这个元素的下标
		}
	}
	void string::erase(size_t pos, size_t len)
	{
		if (len > _size - pos)
		{
			_str[pos] = '\0';
			_size = pos;
		}
		else
		{
			size_t end = pos + len;
			{
				while (end <= _size)
				{
					_str[end - len] = _str[end];
					++end;
				}
				_size -= len;
			}
		}
	}

这是test.cpp文件 

void test_string_find_erase()
{
	string s1("hello world");
	cout << s1.c_str() << endl;
	s1.erase(6,2);
	cout << s1.c_str() << endl;
	s1.erase(6, 20);
	cout << s1.c_str() << endl;
	s1.erase(3);
	cout << s1.c_str() << endl;
	string s2("welcome to guangzhou!");
	cout << s2.find('o') << endl;
	cout << s2.find("guangzh") << endl;

}
int main()
{
	test_string_find_erase();
	return 0;
}

结果如下:

3.迭代器

这是test.cpp文件

void test_string_iterator()
{
	string s1("hello world");
	for (size_t i = 0; i < s1.size(); i++)
	{
		s1[i]++;
		cout << s1[i] << " ";
	}
	cout << endl;
	string::iterator it = s1.begin();
	while (it != s1.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
	for (auto e : s1)
	{
		cout << e;
	}
	cout << endl;

}
int main()
{
	test_string_iterator();
	return 0;
}

运行结果:

4.substr()

string string::substr(size_t pos, size_t len)
{
	size_t leftlen = _size - pos;     //求出要截取部分长度
	if (len > leftlen)
	{
		len = leftlen;
	}
	string tmp;
	tmp.reserve(len);
	for (size_t i = 0; i < len; i++)
	{
		tmp += _str[pos + i];
	}
	return tmp;
}
void test_string5()
{
	string s1("hello world");
	string sub1 = s1.substr(6, 3);
	cout << sub1.c_str() << endl;
	string sub2 = s1.substr(6, 300);
	cout << sub2.c_str() << endl;
	string sub3 = s1.substr(6);
	cout << sub3.c_str() << endl;
	string s2("hello bitxxxxxxxxxxxxxxxxxx");
	s1 = s2;
	cout << s1.c_str() << endl;
	cout << s2.c_str() << endl;
	s1 = s1;
	cout << s1.c_str() << endl;
}
int main()
{
	//test_string_add();
	//test_string_find_erase();
	//test_string_iterator();
	test_string5();
	return 0;
}

5.流插入和提取

	ostream& operator<<(ostream& out, const string& s)
	{
		for (auto ch : s)
		{
			out << ch;
		}
		return out;
	}
	istream& operator>>(istream& in, string& s)
	{
		s.clear();        //此举是为了防止s原有内容对输入的影响
		//类比我们要接满一个大水桶,但是我们不知道需要到底具体有多少水
		// 正好手里有一个可以装N升水的小盆,我们可以用这个小盆装水,满了后导入大桶里
		// 这样可以使得:
		// 输入短串,不会浪费空间
		// 输入长串,避免不断扩容
		const size_t N = 1024;
		char buff[N];
		int i = 0;
		char ch = in.get();       //获取首个单个字符
		while (ch != ' ' && ch != '\n');
		{
			buff[i++] = ch;
			if (i == N - 1)
			{
				buff[i] = '\0';
				s += buff;
				i = 0;
			}
			ch = in.get();    //获取其余诸多单个字符
		}
		//此时有两种情况:1是输入字符串的字符个数正好为N的整数倍,此时i==0;(盆里面没有水了)
		//2是输入字符串的字符个数不为N的整数倍,此时i>0;(盆里面还有水)
		if (i > 0)
		{
			buff[i] = '\0';
			s += buff;
		}
		return in;
	}
	istream& getline(istream& in, string& s, char delim)
	{
		s.clear();
		const size_t N = 1024;
		char buff[N];
		int i = 0;
		char ch = in.get();
		while (ch != delim)
		{
			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;
		}
		return in;
	}
void test_string6()
{
	string s1, s2;
	cin >> s1 >> s2;
	cout << s1 << endl;
	cout << s2 << endl;

	string s3;
	//getline(cin, s3);
	getline(cin, s3, '!');
	cout << s3 << endl;
}
int main()
{
	//test_string_add();
	//test_string_find_erase();
	//test_string_iterator();
	//test_string5();
	test_string6();
	return 0;
}

6.汇总:

这是string.h文件

#pragma once
#include<iostream>
#include<assert.h>
#include<string.h>
using namespace std;
namespace my_string
{
	class string
	{
	public:
		typedef char* iterator;
		typedef const char* const_iterator;

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

		const_iterator end() const
		{
			return _str + _size;
		}

		string(const char* str = "");
		string(size_t n, char ch);
		string(const string& s);
		string& operator=(const string& s);
		~string();
		void clear()
		{
			_str[0] = '\0';
			_size = 0;
		}
		const char* c_str() const
		{
			return _str;
		}
		void reserve(size_t n);
		void push_back(char ch);
		void append(const char* str);
		string& operator+=(char ch);
		string& operator+=(const char* str);
		void insert(size_t pos, size_t n, char ch);
		void insert(size_t pos, const char* str);
		void erase(size_t pos = 0, size_t len = npos);
		size_t find(char ch, size_t pos = 0);
		size_t find(const char* str, size_t pos = 0);
		size_t size()const
		{
			return _size;
		}
		size_t capacity()const
		{
			return _size;
		}
		char& operator[](size_t pos) const
		{
			assert(pos < _size);
			return _str[pos];
		}
		string substr(size_t pos, size_t len = npos);
		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;
	private:
		char* _str;
		size_t _size;
		size_t _capacity;
		const static size_t npos;
	};
	//cout<<s1
	ostream& operator<<(ostream& out, const string& s);
	//cin>>s1
	istream& operator<<(istream& in, string& s);
	istream& getline(istream& is, string& s, char delim = '#');

}

这是string.cpp文件

#define _CRT_SECURE_NO_WARNINGS 1
#include"string.h"
namespace my_string
{
	const size_t string::npos = -1;
	string::string(size_t n, char ch)
		:_str(new char[n + 1])
		, _size(n)
		, _capacity(n)
	{
		for (size_t i = 0; i < n; i++)
		{
			_str[i] = ch;
		}
		_str[_size] = '\0';
	}
	string::string(const char* str)
		:_size(strlen(str))
	{
		_capacity = _size;
		_str = new char[_size + 1];   //多开一个空间放\0
		strcpy(_str, str);
	}
	//s2(s1)
	string::string(const string& s)
	{
		_str = new char[s._capacity + 1];    //永远都记得多开一个
		strcpy(_str, s._str);
		_size = s._size;
		_capacity = s._capacity;
	}
	//s1=s2
	//s1=s1(不建议这样做)
	string& string::operator=(const string& s)
	{
		//this :s1     s:s2
		if (this != &s)     //避免s1=s1这种事件发生
		{
			//这里由于我们之前在构造_str的时候使用new[]了,但为了我们之后将s2拷贝给s1,
			//我们要开一个能装下s2的空间,所以这里我们先delete[],再new[]一个,用于拷贝s2,
			//注意,strcpy不能变插边扩容,这才是我们这么做的根本原因
			delete[] _str;
			_str = new char[s._capacity + 1];
			strcpy(_str, s._str);
			_size = s._size;
			_capacity = s._capacity;
		}
		return *this;          //我们要通过s2构造s1,故返回*this
	}
	string::~string()
	{
		delete[] _str;
		_str = nullptr;
		_size = _capacity = 0;
	}
	void string::reserve(size_t n)
	{
		if (n > _capacity)
		{
			char* tmp = new char[n + 1];
			strcpy(tmp, _str);
			delete[] _str;
			_str = tmp;
			_capacity = n;
		}
	}

	void string::push_back(char ch)
	{
		if (_size + 1 > _capacity)
		{
			//意味着此时已经满了,需要扩容才能插入
			//扩容,建议使用函数复用
			//还要讨论原来容量是不是0
			reserve(_capacity == 0 ? 4 : _capacity * 2);
		}
		//此时已经完成扩容,容量足够用
		_str[_size] = ch;
		_size++;
		_str[_size] = '\0';            //别忘了把\0也考过来
	}
	void string::append(const char* str)
	{
		//注:我们这里是直接按照库里的思路去实现的  
		// 在这里扩容_size+len也是可以的   只不过思路不一样
		// 也可能官方认为追加直接扩二倍  后面人继续使用的时候可以少调几次开空间吧
		size_t len = strlen(str);
		if (_size + len > _capacity)
		{
			//意味着此时已经满了,需要扩容才能插入
			//扩容,与push_back不同的是,
			// 默认使用append是认为你这个字符串原先就是有内容才追加的
			// 如果害怕有人确实会直接使用这个接口 可以加上_capacity=0的情况
			size_t newcapacity = 2 * _capacity;
			//考虑到可能插入的字符串过长,2倍扩容都可能不够
			//为防止越界的产生,我们再严谨的讨论一下
			if (_size + len > 2 * _capacity)
			{
				newcapacity = _size + len;
			}
			reserve(newcapacity);
		}
		//strcpy在拷贝时会从第一个字符出发找\0,
		// 为了节约编译器运行时间,我们直接手动让他从\0出发
		strcpy(_str + _size, str);   
		_size += len; 
	}
	string& string::operator+=(char ch)
	{
		push_back(ch);
		return *this;
	}
	string& string::operator+=(const char* str)
	{
		append(str);
		return *this;
	}
	void string::insert(size_t pos, size_t n, char ch)
	{
		assert(pos <= _size);
		assert(n > 0);
		//还是要考虑扩容问题
		if (_size + n > _capacity)
		{
			size_t newcapacity = 2 * _capacity;
			if (_size + n > 2 * _capacity)
			{
				newcapacity = _size + n;
			}
			reserve(newcapacity);
		}
		size_t end = _size + n;       //一切以\0为准
		while (end > pos + n - 1)     //准备挪动数据,这是需要挪动的数据范围
		{
			_str[end] = _str[end - n];//最后一个数据先动
			end--;
		}
		//挪完了,有地方了,但是要插入的数还没进来呢!
		for (size_t i = 0; i < n; i++)
		{
			_str[pos + i] = ch;
		}
		_size += n;
	}
	void string::insert(size_t pos, const char* str)
	{
		assert(pos <= _size);
		size_t n = strlen(str);
		if (_size + n > _capacity)
		{
			size_t newCapacity = 2 * _capacity;
			if (_size + n > 2 * _capacity)
			{
			    newCapacity = _size + n;
			}
			reserve(newCapacity);
		}
		size_t end = _size + n;
        while (end > pos + n - 1)
        {
	        _str[end] = _str[end - n];
	        --end;
        }
		for (size_t i = 0; i < n; i++)
		{
			_str[pos + i] = str[i];
		}
	}
	void string::erase(size_t pos, size_t len)
	{
		if (len > _size - pos)
		{
			_str[pos] = '\0';
			_size = pos;
		}
		else
		{
			size_t end = pos + len;
			{
				while (end <= _size)
				{
					_str[end - len] = _str[end];
					++end;
				}
				_size -= len;
			}
		}
	}
	size_t string::find(char ch, size_t pos)
	{
		for (size_t i = pos; i < _size; i++)
		{
			if (_str[i] == ch)
			{
				return i;
			}
		}
		return npos;
	}
	size_t string::find(const char* str, size_t pos)
	{
		const char* p = strstr(_str + pos, str);   
		//strstr() 函数的作用是在一个字符串(str1)中查找另一个字符串(str2)的出现位置。
		//如果找到,它返回一个指向 str1 中第一次出现的 str2 的指针;
		// 如果找不到,则返回空指针(NULL)。
		if (p == nullptr)
		{
			return npos;
		}
		else
		{
			return p - _str;      //两个指针相减,结果得到这个元素的下标
		}
	}
	string string::substr(size_t pos, size_t len)
	{
		size_t leftlen = _size - pos;     //求出要截取部分长度
		if (len > leftlen)
		{
			len = leftlen;
		}
		string tmp;
		tmp.reserve(len);
		for (size_t i = 0; i < len; i++)
		{
			tmp += _str[pos + i];
		}
		return tmp;
	}
	bool string::operator==(const string& s)const
	{
		return strcmp(_str, s._str) == 0;
	}
	bool string::operator!=(const string& s)const
	{
		return !(*this == s);
	}
	bool string::operator<(const string& s)const
	{
		return strcmp(_str, s._str) < 0;
	}
	bool string::operator<=(const string& s)const
	{
		return *this < s || *this == s;
	}
	bool string::operator>(const string& s)const
	{
		return !(*this <= s);
	}
	bool string::operator>=(const string& s)const
	{
		return *this == s || *this > s;
	}
	ostream& operator<<(ostream& out, const string& s)
	{
		for (auto ch : s)
		{
			out << ch;
		}
		return out;
	}
	istream& operator>>(istream& in, string& s)
	{
		s.clear();        //此举是为了防止s原有内容对输入的影响
		//类比我们要接满一个大水桶,但是我们不知道需要到底具体有多少水
		// 正好手里有一个可以装N升水的小盆,我们可以用这个小盆装水,满了后导入大桶里
		// 这样可以使得:
		// 输入短串,不会浪费空间
		// 输入长串,避免不断扩容
		const size_t N = 1024;
		char buff[N];
		int i = 0;
		char ch = in.get();       //获取首个单个字符
		while (ch != ' ' && ch != '\n');
		{
			buff[i++] = ch;
			if (i == N - 1)
			{
				buff[i] = '\0';
				s += buff;
				i = 0;
			}
			ch = in.get();    //获取其余诸多单个字符
		}
		//此时有两种情况:1是输入字符串的字符个数正好为N的整数倍,此时i==0;(盆里面没有水了)
		//2是输入字符串的字符个数不为N的整数倍,此时i>0;(盆里面还有水)
		if (i > 0)
		{
			buff[i] = '\0';
			s += buff;
		}
		return in;
	}
	istream& getline(istream& in, string& s, char delim)
	{
		s.clear();
		const size_t N = 1024;
		char buff[N];
		int i = 0;
		char ch = in.get();
		while (ch != delim)
		{
			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;
		}
		return in;
	}
}

这是test.cpp文件

#include"string.h"
#include<string>
void test_string_add()
{
	string s1("hello world");
	cout << s1.c_str() << endl;
	s1 += ' ';
	cout << s1.c_str() << endl;
	s1 += '+';
	cout << s1.c_str() << endl;
	s1 += "hello everybody";
	cout << s1.c_str() << endl;
	s1.push_back(',');
	cout << s1.c_str() << endl;
	s1.append("welcome!");
	cout << s1.c_str() << endl;
	s1.insert(6,1, 't');
	cout << s1.c_str() << endl;
	s1.insert(7, "he ");
	cout << s1.c_str() << endl;
	s1.insert(41, "nice to meet you");
	cout << s1.c_str() << endl;
	s1.insert(0, "good morning!");
	cout << s1.c_str() << endl;
}
void test_string_find_erase()
{
	string s1("hello world");
	cout << s1.c_str() << endl;
	s1.erase(6,2);
	cout << s1.c_str() << endl;
	s1.erase(6, 20);
	cout << s1.c_str() << endl;
	s1.erase(3);
	cout << s1.c_str() << endl;
	string s2("welcome to guangzhou!");
	cout << s2.find('o') << endl;
	cout << s2.find("guangzh") << endl;

}
void test_string_iterator()
{
	string s1("hello world");
	for (size_t i = 0; i < s1.size(); i++)
	{
		s1[i]++;
		cout << s1[i] << " ";
	}
	cout << endl;
	string::iterator it = s1.begin();
	while (it != s1.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
	for (auto e : s1)
	{
		cout << e;
	}
	cout << endl;

}
void test_string5()
{
	string s1("hello world");
	string sub1 = s1.substr(6, 3);
	cout << sub1.c_str() << endl;
	string sub2 = s1.substr(6, 300);
	cout << sub2.c_str() << endl;
	string sub3 = s1.substr(6);
	cout << sub3.c_str() << endl;
	string s2("hello bitxxxxxxxxxxxxxxxxxx");
	s1 = s2;
	cout << s1.c_str() << endl;
	cout << s2.c_str() << endl;
	s1 = s1;
	cout << s1.c_str() << endl;
}
void test_string6()
{
	string s1, s2;
	cin >> s1 >> s2;
	cout << s1 << endl;
	cout << s2 << endl;

	string s3;
	//getline(cin, s3);
	getline(cin, s3, '!');
	cout << s3 << endl;
}
int main()
{
	//test_string_add();
	//test_string_find_erase();
	//test_string_iterator();
	//test_string5();
	test_string6();
	return 0;
}

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

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

相关文章

架构06-分布式共识

零、文章目录 架构06-分布式共识 1、分布式共识 &#xff08;1&#xff09;基本概念 **分布式共识&#xff1a;**在分布式系统中&#xff0c;多个节点之间达成一致的过程。**复杂性来源&#xff1a;**网络的不可靠性和请求的并发性。**应用场景&#xff1a;**如何确保重要数…

USB 声卡全解析:提升音频体验的得力助手

在当今数字化的时代&#xff0c;音频领域的追求愈发多元。无论是热衷聆听高品质音乐的爱好者&#xff0c;还是在专业音频工作中精雕细琢的人士&#xff0c;亦或是在游戏世界里渴望极致音效沉浸的玩家&#xff0c;都始终在寻觅能让音频体验更上一层楼的妙法。而 USB 声卡&#x…

系统--线程互斥

1、相关背景知识 临界资源多线程、多执行流共享的资源,就叫做临界资源临界区每个线程内部,访问临界资源的代码互斥在任何时刻,保证有且只有一个执行流进入临界区,访问临界资源,对临界资源起到保护作用原子性不会被任何调度机制打断的操作,该操作只有两态,要么完成,要么…

【短视频矩阵系统==saas技术开发】

在数字媒体领域&#xff0c;短视频的崛起已不可忽视。对于商业实体而言&#xff0c;掌握如何通过短视频平台有效吸引潜在客户并提高转化率&#xff0c;已成为一项关键课题。本文旨在深入剖析短视频矩阵系统的构成与作用机制&#xff0c;以期为企业提供一套系统化的策略&#xf…

Python语法基础(八)

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” 异常处理 这一个部分&#xff0c;我们来讲一下异常处理这部分。 异常特点 当程序执行的过程中&#xff0c;我们遇到了异常&#xff0c;而且异常未被处理&#xff0c;那么程序…

burp2

声明&#xff01; 学习视频来自B站up主 **泷羽sec** 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他均与本人以及泷羽sec团队无关&a…

linux 获取公网流量 tcpdump + python + C++

前言 需求为&#xff0c;统计linux上得上下行公网流量&#xff0c;常规得命令如iftop 、sar、ifstat、nload等只能获取流量得大小&#xff0c;不能区分公私网&#xff0c;所以需要通过抓取网络包并排除私网段才能拿到公网流量。下面提供了一些有效得解决思路&#xff0c;提供了…

React 路由与组件通信:如何实现路由参数、查询参数、state和上下文的使用

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

爬虫第四篇:Xpath 路径表达式全解析:从网页基础到爬取百度贴吧图片实战

简介&#xff1a;本文围绕 Xpath 路径表达式展开讲解&#xff0c;先是介绍了网页相关基础如 html、css、vue 以及前后端分离的概念与示例&#xff0c;包括各部分的结构、作用及简单代码展示&#xff0c;随后详细阐述了 xml 的节点关系、选取节点、谓语等理论知识&#xff0c;最…

七牛云成功保存但无法显示和访问{“error“:“download token not specified“}

在使用七牛云存储图片时&#xff0c;前端通过链接访问图片时遇到错误&#xff1a; {"error":"download token not specified"} 具体表现为&#xff1a; 后端通过 access_key 和 secret_key 生成了上传和下载的 Token。前端将域名与 res.key 拼接后生成图…

linux下环境变量的使用

文章目录 环境变量一、环境变量的定义与特点二、环境变量的分类三、常用的环境变量四 环境变量相关指令五 c语言获取环境变量接口六 通过代码如何获取环境变量 环境变量 环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数 一、环境变量…

2-2-18-9 QNX系统架构之文件系统(三)

阅读前言 本文以QNX系统官方的文档英文原版资料为参考&#xff0c;翻译和逐句校对后&#xff0c;对QNX操作系统的相关概念进行了深度整理&#xff0c;旨在帮助想要了解QNX的读者及开发者可以快速阅读&#xff0c;而不必查看晦涩难懂的英文原文&#xff0c;这些文章将会作为一个…

基于 MVC 架构的 SpringBoot 高校行政事务管理系统:设计优化与实现验证

摘 要 身处网络时代&#xff0c;随着网络系统体系发展的不断成熟和完善&#xff0c;人们的生活也随之发生了很大的变化&#xff0c;人们在追求较高物质生活的同时&#xff0c;也在想着如何使自身的精神内涵得到提升&#xff0c;而读书就是人们获得精神享受非常重要的途径。为了…

vue3-Import declaration conflicts with local declaration of dayjs

同步发布于我的网站 &#x1f680; 概述错误描述 原代码报错信息 原因分析解决方案 修改导入语句使用泛型 代码解释总结 概述 在使用 Vue3 和 dayjs 时&#xff0c;可能会遇到一个常见的错误&#xff1a;“Import declaration conflicts with local declaration of ‘dayjs’…

【ubuntu-22.04】ubuntu-22.04搭建openwrt-23.05编译环境操作说明

ubuntu-22.04镜像下载 Index of /releases/22.04.1 安装ubuntu系统 安装openwrt-23.05依赖包 sudo apt update sudo apt install build-essential clang flex bison g++ gawk \ gcc-multilib g++-multilib gettext git libncurses-dev libssl-dev \ python3-distutils pyth…

html+css网页设计马林旅行社移动端4个页面

htmlcss网页设计马林旅行社移动端4个页面 网页作品代码简单&#xff0c;可使用任意HTML辑软件&#xff08;如&#xff1a;Dreamweaver、HBuilder、Vscode 、Sublime 、Webstorm、Text 、Notepad 等任意html编辑软件进行运行及修改编辑等操作&#xff09;。 获取源码 1&#…

【算法】位运算合集

阿华代码&#xff0c;不是逆风&#xff0c;就是我疯 你们的点赞收藏是我前进最大的动力&#xff01;&#xff01; 希望本文内容能够帮助到你&#xff01;&#xff01; 目录 零&#xff1a;位运算基础公式 零&#xff1a;五道基础题 1&#xff1a;位1的个数 2&#xff1a;比…

Android 硬件抽象层(HAL)全解析:智能设备硬件协同揭秘

在Android硬件抽象层&#xff08;HAL&#xff09;开发中&#xff0c;需要掌握许多底层技术&#xff0c;并熟悉如何将硬件驱动与Android系统的上层应用接口相集成。以下是HAL开发中需要掌握的核心技术和一些示例代码&#xff0c;以帮助理解其实现原理&#xff1a; 1. C/C编程和…

Linux如何将文件或目录打成rpm包?-- rpmbuild打包详解

&#x1f468;‍&#x1f393;博主简介 &#x1f3c5;CSDN博客专家   &#x1f3c5;云计算领域优质创作者   &#x1f3c5;华为云开发者社区专家博主   &#x1f3c5;阿里云开发者社区专家博主 &#x1f48a;交流社区&#xff1a;运维交流社区 欢迎大家的加入&#xff01…

推荐学习笔记:矩阵补充和矩阵分解

参考&#xff1a; 召回 fun-rec/docs/ch02/ch2.1/ch2.1.1/mf.md at master datawhalechina/fun-rec GitHub 业务 隐语义模型与矩阵分解 协同过滤算法的特点&#xff1a; 协同过滤算法的特点就是完全没有利用到物品本身或者是用户自身的属性&#xff0c; 仅仅利用了用户与…