C++之String类模拟实现(下)

片头

哈喽~小伙伴们,在上一篇中,我们讲解了C++的string类的相关函数,这一章中,我们将继续深入学习string类函数,准备好了吗?咱们开始咯~


五、对内容进行修改

⑤insert函数

在指定位置插入字符或者字符串

函数声明

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

 函数定义

	//insert函数,插入字符void string::insert(size_t pos, char ch) {//严格控制pos的取值范围,避免越界assert(pos <= _size);//如果_capacity和_size相等,需要扩容if (_capacity == _size) {size_t newCapacity = _capacity == 0 ? 4 : 2 * _capacity;reserve(newCapacity);}//end从'\0'的位置开始size_t end = _size;while (end >= pos) {_str[end + 1] = _str[end];//最后一次: pos+1 = pos;--end;//每执行完一次,end向前挪动一位}_str[pos] = ch;//将pos位置放入ch字符++_size;	   //_size更新}

这里的end初始指向末尾_size,也就是'\0'的位置,将数据不断往后挪动,直到腾出pos位置。 

测试一下:

但是如果这时,我们往下标为0的位置头插'D',发现编译器崩溃,为啥呢?

因为pos此时为0, 如果pos大于等于0,则进入循环,若pos小于0,就结束。end是size_t类型,就不会小于0。如果end减到-1,再转成无符号类型size_t,无符号整型-1是一个很大很大的数,必然会越界。所以,我们将end的类型修改为int。

int end = _size;

 

但是此时的pos仍然是size_t类型,在一个操作符两边的操作数,如果它们类型不一样,它们就会发生隐式类型转换,当有符号类型遇到无符号类型,有符号类型就会隐式类型转换成无符号类型。

解决方法1:将pos强转成int类型

        int end = _size;                 //end从'\0'的位置开始while (end >= (int)pos)          //将pos强转成int,避免类型不同{       _str[end + 1] = _str[end];   //最后一次:_str[pos+1] = _str[pos]--end;}_str[pos] = ch;++_size;	  }

解决方法2:end仍然是size_t类型,但是起始位置是_size+1  

		size_t end = _size + 1;			//end的起始位置是_size的下一个位置while (end > pos)				//end>pos位置继续,end==pos就结束{_str[end] = _str[end - 1];	//最后一次: 下标为0的元素挪到下标为1的位置end--;}_str[pos] = ch;++_size;	

我们 初始指向'\0'的下一个位置,最终当end和pos值相等时跳出循环,完成插入与对成员变量的修改。


接下来,我们用insert函数实现插入字符串

函数声明

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

函数定义

//方法1:	void string::insert(size_t pos, const char* str) {//严格控制pos的取值范围,避免越界assert(pos <= _size);int len = strlen(str);			//新字符串的长度(不包括'\0')if (_size + len > _capacity)	//检查是否需要扩容{reserve(_size + len);}int end = _size;				//end的起始位置_sizewhile (end >= (int)pos)			//判断end是否满足循环条件{_str[end + len] = _str[end];end--;}memcpy(_str + pos, str, len);	//使用memcpy函数,拷贝len个字符,不包括'\0'_size += len;					//_size更新}

诶,为啥这里不能使用strcpy函数了呢?

因为strcpy函数会把插入字符串的'\0'一起拷贝过去,改变了原字符串的内容,这样就不行的。

中间的逻辑也可以这样进行修改,保证结果正确:

		int end = _size + len;				  //将end设定为_size+lenwhile (end > pos + len - 1)	          //判断end是否满足循环条件{_str[end] = _str[end - len];end--;}memcpy(_str + pos, str, len);		 //使用memcpy函数,拷贝len个字符,不包括'\0'_size += len;						 //_size更新

 测试一下:

实现了insert函数后,我们就可以在push_back函数和append函数复用它们

//尾插一个字符void string::push_back(char ch) {insert(_size, ch);	//在_size位置,插入一个字符ch}//尾插一个字符串void string::append(const char* str) {insert(_size, str);//在_size位置,插入一个字符串}

⑥erase函数

函数声明

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

从pos位置开始,删除len个字符(pos默认为0,len默认为npos)

这里的pos不能等于_size,因为我们不能删去'\0',npos为整数-1,这里我们需要自己在类中定义。

public:static const int npos;

在外部进行初始化

const int string::npos = -1;

函数定义

//erase函数void string::erase(size_t pos, size_t len = npos) {assert(pos < _size);				 //严格控制pos的有效区间//len大于后面字符个数时,有多少删多少if (len == npos || len >= _size - pos) {_str[pos] = '\0';				//将pos的位置改为'\0'_size = pos;					//有效元素的个数为pos个}else{//len小于后面的字符个数strcpy(_str + pos, _str + pos + len);//将后面的字符拷贝到pos位置_size -= len;						 //有效字符个数更新}}

运行一下:


⑦find查找字符或字符串

函数声明

		//find函数size_t find(char ch, size_t pos = 0);			//查找字符size_t find(const char* sub, size_t pos = 0);	//查找字符串

函数定义

//从pos位置开始,查找字符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* sub, size_t pos) {char* p = strstr(_str + pos, sub);return p - _str;//返回的就是下标}

(1)查找字符:我们给了缺省值npos,如果不赋值,默认从起始位置开始找,遍历数组;若找到目标字符,则返回下标;若找不到,则返回npos 

(2)查找目标字符串:我们调用字符串函数strstr,如果找到返回目标字符串起始位置的指针;若没找到,则返回空指针。我们再加一个判断条件,如果不为空,指针相减即为目标字符串起始位置的下标(第一个元素的下标);若为空,则找不到,返回npos。

因此,我们还可以把查找字符串函数再改进一下:

//从pos位置开始,查找字符串size_t string::find(const char* sub, size_t pos) {//p指针表示找到目标字符串的地址char* p = strstr(_str + pos, sub);//如果p指针存在,直接返回目标字符串的下标if (p) {return p - _str;}else {//如果p指针不存在,返回nposreturn npos;}}

运行一下


⑧substr函数

substr函数用于从字符串中提取一个子字符串

函数声明

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

函数定义

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

我们直接在新的sub字符串尾插字符

还有另外一种定义方法:

//substr函数//定义方法2:string string::substr(size_t pos, size_t len) {if (len == npos || len >= _size - pos) {string  sub(_str + pos);return sub;}else {string sub;sub.reserve(len);//至少开len个字符的空间for (int i = 0; i < len; i++) //执行拷贝的次数{sub += _str[pos + i];//从pos位置开始,拷贝len个字符}return sub;}}

⑨swap函数

 函数声明

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

函数定义

//swap函数void string::swap(string& s) {//使用std库里面的swap函数std::swap(_str, s._str);		std::swap(_capacity, s._capacity);std::swap(_size, s._size);}

运行一下


 六、重载函数

①operator=赋值函数

函数声明

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

函数定义

//operator=赋值函数string& string::operator=(const string& s) {char* temp = new char[s._capacity + 1];		//多开一个空间,存放'\0'strcpy(temp, s._str);						//数据拷贝delete[] _str;								//释放原来的空间_str = temp;								//将指针指向新地址_size = s._size;							//_size更新_capacity = s._capacity;					//_capacity更新return *this;								//返回*this}

运行一下

但是如果是自己赋值给自己,那么就不需要释放原来的旧空间。因此,我们需要将代码改进

//operator=赋值函数string& string::operator=(const string& s) {if (this != &s) //如果不是自身赋值{char* temp = new char[s._capacity + 1];		//多开一个空间,存放'\0'strcpy(temp, s._str);						//数据拷贝delete[] _str;								//释放原来的空间_str = temp;								//将指针指向新地址_size = s._size;							//_size更新_capacity = s._capacity;					//_capacity更新}return *this;								//返回*this}
}

开辟一块新空间,将原内容拷贝到新空间中并释放原来的空间,然后更改指针指针指向和成员变量,最后返回*this 


②operator==等几个比较函数

函数声明

		//比较函数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;

函数定义

   //比较2个字符串元素的ASCII码值bool string::operator<(const string& s)const {return strcmp(_str, s._str) < 0;}bool string::operator==(const string& s)const {return strcmp(_str, s._str) == 0;}

我们可以先写出2个简单的函数,剩下的情况可以复用上面的函数

其余的函数如下:

//比较函数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);}bool string::operator!=(const string& s)const {return !(*this == s);}

③流插入和流提取

注意:流插入和流提取不能写在string类里面。比如:cout<<d1,这样只能传递2个参数,如果放到了成员函数,就会多一个this指针,这个参数就不方便传递。因此,我们把它写在类外面。

函数声明

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

函数定义

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

我们不需要将上述函数设置为友元,因为我们没有访问成员变量,是通过迭代器来实现的

//流插入//字符串清理void string::clear() {_str[0] = '\0';_size = 0;}istream& operator>>(istream& is, string& str) {str.clear();char ch;ch = is.get();char buff[128];	//缓冲数组size_t i = 0;while (ch != ' ' && ch != '\n') //读字符读不到空格{buff[i++] = ch;if (i == 127) {buff[127] = '\0';str += buff;i = 0;}//s += ch;ch = is.get();}if (i > 0) {buff[i] = '\0';str += buff;}return is;}

输入是对内容的覆盖,所以我们首先实现一个clear函数来清空字符串,原理很简单:

//字符串清理void string::clear() {_str[0] = '\0';_size = 0;}

只需要修改_size并设置'\0'即可

注意,我们cin输入是读不到空格和换行符的。

其中,ch = is.get();从输入流中读取单个字符

我们这里创造了一个缓冲数组char buff[128];,来简单进行讲解一下:

这个数组功能就是存储字符的内容,如果数组满了,则直接进行+=将整个数组内容尾插到字符串中,并更新索引,其主要特点就是,避免了我们自己为字符串s不断扩容降低了效率,并且可能会造成空间的浪费

与cin不同的是,getline可以读取空格,所以我们只需要修改循环条件即可

istream& getline(istream& in, string& s) {s.clear();char ch;ch = in.get();char buff[128];size_t i = 0;while (ch != '\0') {buff[i++] = ch;if (i == 127) {buff[127] = '\0';s += buff;i = 0;}//s += ch;ch = in.get();}if (i > 0) {buff[i] = '\0';s += buff;}return in;}

七、完整代码 

string.h文件

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include<string.h>
#include<assert.h>namespace bit
{class string{public:typedef char* iterator;//将char*重命名为iteratortypedef const char* const_iterator;//非const版本的iteratoriterator begin();	   //提供iterator begin()函数iterator end();		   //提供iterator end()函数//const版本的iteratorconst_iterator begin() const;	const_iterator end() const;//string();//无参构造string(const char* str = "");	//全缺省的构造函数~string();						//析构函数string(const string& s);		//拷贝构造函数const char* c_str() const;		//c_str函数size_t size() const;			//size()函数size_t capacity() const;		//capacity()函数//非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);//find函数size_t find(char ch, size_t pos = 0);			//查找字符size_t find(const char* sub, size_t pos = 0);	//查找字符串//substr函数string substr(size_t pos = 0, size_t len = npos);//operator=赋值函数string& operator=(const string& s);//swap函数void swap(string& s1);//比较函数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;//字符串清理void clear();private:char* _str;size_t _size;size_t _capacity;//静态成员变量npos在类里面声明const static size_t npos;};//流插入和流提取istream& operator>>(istream& is, string& str);ostream& operator<<(ostream& os, const string& str);//getline函数模拟流输入istream& getline(istream& in, string& s);
}

 string.cpp文件

#include"string.h"
namespace bit {//静态成员变量npos在类外定义const size_t string::npos = -1;//指定类域,在类里面声明,在类外面定义//begin()函数返回的是第一个元素string::iterator string::begin() {return _str;}//end()函数返回的是最后一个元素的下一个位置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() {
//	//_str = new char('\0');
//	_str = new char[1]{'\0'};
//	_size = 0;
//	_capacity = 0;
//}	//构造函数string::string(const char* str)//将_size放在初始化列表里面:_size(strlen(str)){//_str和_capacity放到函数体里面_str = (new char[_size + 1]);_capacity = _size;strcpy(_str, str);}//析构函数string::~string() {delete[] _str;_str = nullptr;_size = _capacity = 0;}//拷贝构造函数string::string(const string& s) {_str = new char[s._capacity + 1];	//多开1个空间,存放'\0'strcpy(_str, s._str);				//拷贝数据_capacity = s._capacity;			//设置容量_size = s._size;					//设置有效数据的个数}//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;}//operator[]函数char& string::operator[](size_t pos) {assert(pos < _size);//严格控制pos的有效区间return _str[pos];}//const版本const char& string::operator[](size_t pos)const {assert(pos < _size);return _str[pos];}void string::reserve(size_t n) {//如果n大于当前的_capacity,需要扩容if (n > _capacity) {//开n+1个空间,多开1个空间预留给'\0'//'\0'是不包括在_capacity里面的char* temp = new char[n + 1];strcpy(temp, _str);				//拷贝数据delete[] _str;					//释放旧空间_str = temp;					//将新的地址赋给_str_capacity = n;					//_capacity为n,代表n个有效数据}}//尾插一个字符void string::push_back(char ch) {如果_capacity == _size,说明空间为0或者空间满了,需要扩容//if (_capacity == _size) {//	size_t newCapacity = _capacity == 0 ? 4 : 2 * _capacity;//	reserve(newCapacity);//}//_str[_size] = ch;			//新的字符ch插入到原来存放'\0'的位置//_str[_size + 1] = '\0';		//'\0'就存放到下一个位置//_size++;					//_size更新insert(_size, ch);	//在_size位置,插入一个字符ch}//尾插一个字符串void string::append(const char* str) {获取str新字符串的长度//size_t len = strlen(str);如果_size+len大于原有的capacity,扩容//if (_size + len > _capacity) {//	reserve(_size + len);//}strcat(_str, str);//在原来的基础上,尾插str新字符串//strcpy(_str+_size, str);//自定义起始位置,从'\0'的位置开始//_size += len;			//_size更新insert(_size, str);//在_size位置,插入一个字符串}string& string::operator+=(char ch) {push_back(ch);//调用push_back函数return *this;}string& string::operator +=(const char* str) {append(str);//调用append函数return *this;}//insert函数void string::insert(size_t pos, char ch) {//严格控制pos的取值范围,避免越界assert(pos <= _size);//如果_capacity和_size相等,需要扩容if (_capacity == _size) {size_t newCapacity = _capacity == 0 ? 4 : 2 * _capacity;reserve(newCapacity);}end从'\0'的位置开始//int end = _size;将pos强转成int,避免类型不同//while (end >= (int)pos) {//	_str[end + 1] = _str[end];//	//最后一次: pos+1 = pos;//	--end;//	//每执行完一次,end向前挪动一位//}size_t end = _size + 1;			//end的起始位置是_size的下一个位置while (end > pos)				//end>pos位置继续,end==pos就结束{_str[end] = _str[end - 1];	//最后一次: 下标为0的元素挪到下标为1的位置end--;}_str[pos] = ch;//将pos位置放入ch字符++_size;	   //_size更新}void string::insert(size_t pos, const char* str) {//严格控制pos的取值范围,避免越界assert(pos <= _size);int len = strlen(str);				//新字符串的长度(不包括'\0')if (_size + len > _capacity)		//检查是否需要扩容{reserve(_size + len);}//int end = _size;					//end的起始位置_size//while (end >= (int)pos)				//判断end是否满足循环条件//{//	_str[end + len] = _str[end];//	end--;//}int end = _size + len;				  //将end设定为_size+lenwhile (end > pos + len - 1)	      //判断end是否满足循环条件{_str[end] = _str[end - len];end--;}memcpy(_str + pos, str, len);		//使用memcpy函数,拷贝len个字符,不包括'\0'_size += len;						//_size更新}//erase函数void string::erase(size_t pos, size_t len) {assert(pos < _size);				 //严格控制pos的有效区间//len大于后面字符个数时,有多少删多少if (len == npos || len >= _size - pos) {_str[pos] = '\0';				//将pos的位置改为'\0'_size = pos;					//有效元素的个数为pos个}else{//len小于后面的字符个数strcpy(_str + pos, _str + pos + len);//将后面的字符拷贝到pos位置_size -= len;						 //有效字符个数更新}}//find函数//从pos位置开始,查找字符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* sub, size_t pos) {//char* p = strstr(_str + pos, sub);//return p - _str;//返回的就是下标//p指针表示找到目标字符串的地址char* p = strstr(_str + pos, sub);//如果p指针存在,直接返回目标字符串的下标if (p) {return p - _str;}else {//如果p指针不存在,返回nposreturn npos;}}//substr函数string string::substr(size_t pos, size_t len) {string sub;if (len == npos || len >= _size - pos) {for (size_t i = pos; i < _size; i++) {sub += _str[i];}}else {for (size_t i = pos; i < pos + len; i++) {sub += _str[i];}}return sub;}//substr函数//string string::substr(size_t pos, size_t len) {//	if (len == npos || len >= _size - pos) {//	string  sub(_str + pos);//		return sub;//	}//	else {//		string sub;//		sub.reserve(len);//至少开len个字符的空间//		for (int i = 0; i < len; i++) //执行拷贝的次数//		{//			sub += _str[pos + i];//从pos位置开始,拷贝len个字符//		}//		return sub;//	}//}//operator=赋值函数string& string::operator=(const string& s) {if (this != &s) //如果不是自身赋值{char* temp = new char[s._capacity + 1];		//多开一个空间,存放'\0'strcpy(temp, s._str);						//数据拷贝delete[] _str;								//释放原来的空间_str = temp;								//将指针指向新地址_size = s._size;							//_size更新_capacity = s._capacity;					//_capacity更新}return *this;								//返回*this}//swap函数void string::swap(string& s) {//使用std库里面的swap函数std::swap(_str, s._str);		std::swap(_capacity, s._capacity);std::swap(_size, s._size);}//比较函数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);}bool string::operator==(const string& s)const {return strcmp(_str, s._str) == 0;}bool string::operator!=(const string& s)const {return !(*this == s);}//字符串清理void string::clear() {_str[0] = '\0';_size = 0;}//流插入istream& operator>>(istream& is, string& str) {str.clear();char ch;ch = is.get();char buff[128];	//缓冲数组size_t i = 0;while (ch != ' ' && ch != '\n') //读字符读不到空格{buff[i++] = ch;if (i == 127) {buff[127] = '\0';str += buff;i = 0;}//s += ch;ch = is.get();}if (i > 0) {buff[i] = '\0';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;}istream& getline(istream& in, string& s) {s.clear();char ch;ch = in.get();char buff[128];size_t i = 0;while (ch != '\0') {buff[i++] = ch;if (i == 127) {buff[127] = '\0';s += buff;i = 0;}//s += ch;ch = in.get();}if (i > 0) {buff[i] = '\0';s += buff;}return in;}}

片尾

今天,在上一篇的基础上,我们深入学习了string类函数的模拟实现,希望看完这篇文章能对友友们有所帮助!!!

点赞收藏加关注!!!

谢谢大家!!!

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

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

相关文章

docker安装elasticsearch(es)+kibana

目录 docker安装elasticsearch 一.准备工作 1.打开docker目录 2.创建elasticsearch目录 3.打开elasticsearch目录 4.拉取elasticsearch镜像 5.检查镜像 二.挂载目录 1.创建数据挂载目录 2.创建配置挂载目录 3.创建插件挂载目录 4.权限授权 三.编辑配置 1.打开con…

重学SpringBoot3-集成Spring Boot Actuator

更多SpringBoot3内容请关注我的专栏&#xff1a;《SpringBoot3》 期待您的点赞&#x1f44d;收藏⭐评论✍ 重学SpringBoot3-集成Spring Boot Actuator 1. 什么是 Spring Boot Actuator&#xff1f;2. Spring Boot Actuator 的核心功能3. Spring Boot 3 中集成 Actuator3.1 添加…

Thread类的基本用法

一.线程创建 继承Thread来创建一个线程 实现 Runnable 接⼝ 匿名内部类创建 Thread ⼦类对象 匿名内部类创建 Runnable ⼦类对象 lambda 表达式创建 Runnable ⼦类对象 二.线程中断 ⽬前常⻅的有以下两种⽅式&#xff1a; 1. 通过共享的标记来进⾏沟通 2. 调⽤ interrup…

计算机网络(以Linux讲解)

计算机网络 网络协议初识协议分层OSI七层模型TCP/IP五层模型--初识 网络中的地址管理IP地址MAC地址 网络传输基本流程网络编程套接字预备知识网络字节序socket编程UDP socketTCP socket地址转换函数Jsoncpp 进程间关系与守护进程进程组会话控制终端作业控制守护进程 网络命令TC…

数学建模算法与应用 第15章 预测方法

目录 15.1 微分方程模型 Matlab代码示例&#xff1a;求解简单的微分方程 15.2 灰色预测模型&#xff08;GM&#xff09; Matlab代码示例&#xff1a;灰色预测模型 15.3 自回归模型&#xff08;AR&#xff09; Matlab代码示例&#xff1a;AR模型的预测 15.4 指数平滑法 M…

openrtp ps流和纯rtp流

改进openrtp 程序 OpenRtp 以RTP协议为主&#xff0c;发送音视频&#xff0c;因为工作量比较大&#xff0c;所以耽误了一些时间&#xff0c;前面一直没有时间来修改这个程序&#xff0c;也有一些人提了意见&#xff0c;现在重视一下&#xff0c;把这个程序做好&#xff0c;让学…

NirCmd-sendkeysendkeypress

引入script [Script File] This command allows you to execute a sequence of commands stored inside the file specified in [Script File] parameter. Example: script "c:\temp\msg.ncl" Example for a script:infobox "Hello !" "This is the …

大型生物制药企业如何实现安全又高效地跨网域数据传输?

大型生物制药企业由于组织结构庞大、业务覆盖研发、生产及销售&#xff0c;因此内部会再细分为多个管理单位&#xff0c;包括研发部门、生产部门、质量控制部门、供应链管理部门及营销部和日常业务支撑部门等。在物理区域划分上&#xff0c;大型生物制药企业会设立实验室、研发…

下载相应版本的PyTorch

1、前置条件 下载某个版本的Python&#xff0c;本文涉及的Python版本为3.10 2、查看该Python版本可以下载的whl文件格式 pip debug --verbose 从上图可以发现python3.10可以下载格式为cp310-cp310-win_amd64的whl文件 PyTorch各稳定版本下载链接&#xff1a;https://downloa…

【工具】音视频翻译工具基于Whisper+ChatGPT

OpenAI推出的开源语音识别工具Whisper&#xff0c;以其卓越的语音识别能力&#xff0c;在音频和视频文件处理领域大放异彩。与此同时&#xff0c;ChatGPT也在翻译领域崭露头角&#xff0c;其强大的翻译能力备受赞誉。因此&#xff0c;一些字幕制作团队敏锐地捕捉到了这两者的结…

Nginx UI 一个可以管理Nginx的图形化界面工具

Nginx UI 是一个基于 Web 的图形界面管理工具&#xff0c;支持对 Nginx 的各项配置和状态进行直观的操作和监控。 Nginx UI 的功能非常丰富&#xff1a; 在线查看服务器 CPU、内存、系统负载、磁盘使用率等指标 在线 ChatGPT 助理 一键申请和自动续签 Let’s encrypt 证书 在…

Flink 批作业如何在 Master 节点出错重启后恢复执行进度?

摘要&#xff1a;本文撰写自阿里云研发工程师李俊睿&#xff08;昕程&#xff09;&#xff0c;主要介绍 Flink 1.20 版本中引入了批作业在 JM failover 后的进度恢复功能。主要分为以下四个内容&#xff1a; 背景解决思路使用效果如何启用 一、背景 在 Flink 1.20 版本之前&am…

react antd redux 全局状态管理 解决修改菜单状态 同步刷新左侧菜单

npm i react-redux1.src新建两个文件 globalState.js 全局状态定义 store.js 全局存储定义 2.globalState.js import { createSlice } from "reduxjs/toolkit";export const globalState createSlice({name: "globalState",initialState: { data: {} },r…

rpa批量发送邮件如何通过编辑器编发邮件?

rpa批量发送邮件的技巧&#xff1f;怎么使用rpa邮箱群发助手&#xff1f; 手动发送邮件变得越来越繁琐且效率低下。为了解决这一问题&#xff0c;越来越多的企业开始采用RPA技术来批量发送邮件。AokSend将详细探讨如何通过编辑器来实现rpa批量发送邮件的功能&#xff0c;从而提…

微信小程序处理交易投诉管理,支持多小程序,一键授权模式

大家好&#xff0c;我是小悟 1、问题背景 玩过微信小程序生态的&#xff0c;或许就有这种感受&#xff0c;如果收到投诉单&#xff0c;不会及时通知到手机端&#xff0c;而是每天早上10:00向小程序的管理员及运营者推送通知。通知内容为截至前一天24时该小程序账号内待处理的交…

计算机视觉之YOLO算法基本原理和应用场景

YOLO算法基本原理 整体流程 YOLO 将目标检测问题转化为一个回归问题。它将输入图像划分成多个网格单元&#xff0c;每个网格单元负责预测中心点落在该网格内的目标。对于每个网格单元&#xff0c;YOLO 预测多个边界框以及这些边界框中包含目标的类别概率。边界框通常由中心点坐…

前端开发笔记--css 黑马程序员1

文章目录 1. css 语法规范2.css的书写风格3.基础选择器选择器的分类标签选择器类选择器类选择器的特殊使用--多类名 id 选择器 字体属性常见字体字体大小字体粗细字体倾斜字体的复合简写字体属性总结 文本属性文本颜色文本对齐装饰文本文本缩进文本间距文本属性总结 css的引入方…

【机器学习】知识总结1(人工智能、机器学习、深度学习、贝叶斯、回归分析)

目录 一、机器学习、深度学习 1.人工智能 1.1人工智能概念 1.2人工智能的主要研究内容与应用领域 1.2.1主要研究内容&#xff1a; 1.2.2应用领域 2.机器学习 2.1机器学习的概念 2.2机器学习的基本思路 2.3机器学习的分类 3.深度学习 3.1深度学习的概念 3.2人工智能…

Java体系中的泛型

1. 泛型 一般的类和方法&#xff0c;只能够使用基本类型&#xff0c;要么是自定义的类&#xff0c;如果要编写可以应用于多种数据类型的代码&#xff0c;这种刻板的限制对代码的约束就会很大&#xff0c;那么如何实现可应用于多种数据类型的代码&#xff0c;而不局限于单一一种…

服务器数据恢复—EMC存储RAID5磁盘阵列数据恢复案例

服务器数据恢复环境&#xff1a; 一台EMC某型号存储设备&#xff0c;该存储中有一组由12块&#xff08;包括2块热备盘&#xff09;STAT硬盘组建的raid5阵列。 服务器故障&#xff1a; 该存储在运行过程中突然崩溃&#xff0c;raid瘫痪。数据恢复工程师到达现场对故障存储设备进…