37 string类关键函数的模拟实现

目录

一、string类的基本框架

二、基本函数的实现

(一)构造函数

1、无参构造

2、带参构造

3、无参与带参的合并

(二)遍历函数

1、有效字符个数与operator[]

2、迭代器

三、高级函数的实现

(一)插入函数

1、扩容操作reserve

2、push_back

3、append

4、operator+=

(1)插入字符

(2)插入字符串

5、insert

(1)插入字符

(2)插入字符串

四、删除函数的实现

五、查找函数的实现

六、截取函数的实现

七、运算符重载

八、整体代码


一、string类的基本框架

        string模拟实现的底层结构设计为字符顺序表,又与顺序表不同,它有'\0',又有自己专用的接口,命名为string类会与库中自带的string类有命名冲突,可以把自己写的类放进自定义名字的命名空间中。string类的基本框架模拟实现如下:

#pragma once
#include<iostream>
#include<assert.h>
using namespace std;namespace zyb
{class string{public:private:size_t _size;size_t _capacity;char* _str;};
}

二、基本函数的实现

(一)构造函数

1、无参构造

        不能把 _str、_size和_capacity粗暴地初始化为nullptr、0和0,因为库中的string类创造空对象后使用c_str可以打印空字符,程序不会崩溃,而初始化为nullptr后,使用c_str函数后进行打印,程序会崩溃:

string s1; 
cout << s1.c_str() << endl;

        因为这样是打印s1中的字符串_str,而打印字符串是遇到'\0'才终止,就要对函数返回的指针进行解引用找到内容中的'\0',但s1是个空指针,就会造成空指针解引用报错

        所以就要给初始化的值为:new charp[1]{'\0'},但为什么不写成new char('\0')?

                ① 用于后续扩容的操作

                ② 为了与带参构造开辟方式进行统一,便于析构。在有参构造中需要new多个char,使用的是 new[],在析构函数中使用delete[]与new[]匹配,对无参构造与有参构造的动态申请的资源的清除。

        无参构造函数的模拟实现如下:

string::string():_str(new char[1] {'\0'}), _size(0), _capacity(0)
{}

2、带参构造

        因为传入的是一个字符串,此时_str的写法为:

_str(new char[strlen(str)+1])

        因为strlen是不计算'\0'的长度,需要加一来补上'\0'的长度。

        空间开辟后就要进行数据的拷贝,需要在函数体中解决,构造如下:

string(const char* str):_str(new char[strlen(str)+1]),_size(strlen(str)),_capacity(strlen(str))
{strcpy(_str, str);
}

        因为strlen是一个io函数,与sizeof在编译时运算不同,strlen是在运行时计算的,在运行时计算会容易出风险,比如:缓冲区溢出、效率问题、字符串内容被意外修改等风险。尽量减少使用strlen进行初始化。

        改进:

string(const char* str):_size(strlen(str)),_capacity(_size),_str(new char[_size+1])
{strcpy(_str, str);
}

        但这也具有一定风险:初始化顺序是根据成员变量定义的顺序来进行的,这就会导致随机值的出现

        解决:修改成员变量定义的顺序。但后续代码被修改后风险依旧存在。

        再改进:只留_size在初始化列表,剩下的成员变量在结构体中进行初始化,这样就不用担心成员变量在定义时被打乱位置而造成的构造错误与多次调用strlen的问题,代码如下:

string(const char* str):_size(strlen(str))
{_capacity = _size;_str = new char[_size + 1];strcpy(_str, str);
}

        虽然在构造的时候_capacity与_str还是会走初始化列表,并且编译器会给一些默认值,但它们接下来会进入函数体中,把这些值进行修正,并不会影响最后的结果。

注意:在分离声明和定义的时候,声明的地方是有命名空间的,在定义的时候也可以使用同名的命名空间,编译器在编译的时候会把他们合为一体。定义函数时还是需要在命名空间中指定类域

        

3、无参与带参的合并

        构造函数最好提供一个全缺省的参数的,这样就可以把无参构造与带参构造合并起来,只需如下定义:

string(const char* str = "");

        直接使用 "" 作为缺省值即可,因为常量字符串后面是自带一个'\0'的

        注意一点:若声明与定义分离,则只能在声明处给缺省值

string.h文件

#pragma once
#include<iostream>
#include<assert.h>
using namespace std;namespace zyb
{class string{public://一、构造函数string(const char* str = "");private:size_t _size;size_t _capacity;char* _str;};
}

string.cpp文件

namespace zyb
{//一、构造函数string::string(const char* str):_size(strlen(str)){_capacity = _size;_str = new char[_size + 1];strcpy(_str, str);}
}

(二)遍历函数

1、有效字符个数与operator[]

//二、遍历操作
//(一)提供下标的最大值_size
size_t size() const
{return _size;
}//(二)普通版本的operator
char& operator[](size_t i)
{assert(i < _size);//判断是否在合理的范围之内return _str[i];
}
//(三)const版本的operator
const char& operator[](size_t i) const
{assert(i < _size);return _str[i];
}

        const版本的返回值不能被修改,所以要用const修饰返回值。

2、迭代器

        因为string的底层是一个数组,因此可以使用一个字符指针来对迭代器进行模拟实现:

using iterator = char*;
// typedef char* iterator;

        范围for对迭代器是有要求的,对应的只能是begin函数,大小写字母不能改变,必须按照命名规则去写。

        常量迭代器:

        为什么是const_iterator,而不是const修饰iterator?

        因为const_iterator表示的是迭代器所指向的内容不能修改,const修饰iterator表示的是迭代器不能修改

string.h文件

using iterator = char*;
using const_iterator = const char*;
// 1、begin
iterator begin()
{return _str;// begin返回首元素的迭代器
}
// 2、begin
iterator end()
{return _str + _size;// end返回末尾元素的下一个位置的迭代器
}
// 3、const迭代器的begin
const_iterator begin() const
{return _str;
}
// 4、const迭代器的begin
const_iterator end() const
{return _str + _size;
}

三、高级函数的实现

(一)插入函数

1、扩容操作reserve

//扩容
void string::reserve(size_t n)
{if (n > _capacity){char* temp = new char[n + 1];//+1是给'\0'留位置strcpy(temp, _str);delete[] _str;_str = temp;_capacity = n;}
}

2、push_back

//push_back
void string::push_back(char ch)
{if (_size == _capacity)//有效字符数与总容量相等就要进行扩容{reserve(_capacity == 0 ? 4 : _capacity * 2);//扩容,使用三目操作符,需要判断总空间是否为0的情况}_str[_size] = ch;_size++;
}

3、append

//append
void string::append(const char* str)
{size_t len = strlen(str);if (_size + len > _capacity){size_t NewCapacity = _capacity * 2;if (_size + len > NewCapacity)//括两倍不够,则需要多少就括多少NewCapacity = _size + len;reserve(NewCapacity);}strcpy(_str + _size, str);_size += len;
}

4、operator+=

        对push_back与append进行复用即可

(1)插入字符
string& string::operator+=(char ch)
{push_back(ch);return *this;
}
(2)插入字符串
string& string::operator+=(char ch)
{push_back(ch);return *this;
}

5、insert

(1)插入字符
void string::insert(size_t pos, char ch)
{//扩容逻辑与push_back相同if (_size == _capacity)reserve(_capacity == 0 ? 4 : _capacity*2);//insert需要向后挪动数据rsize_t end = _size;while (end >= pos){_str[end + 1] = _str[end];end--;}//插入数据_str[pos] = ch;_size++;
}

        这样写的话在pos等于0时会造成越界问题与死循环的出现,因为pos与end都等于0时,end会--,变成-1,而size_t类型的-1又是一个很大的数,就会造成越界访问。

        断点调试小技巧,想要定条件判断断点,直接写一段无意义的代码,把判断条件写成想要的那个条件就行,例如:

if(end == 0)int i = 0;//在此处打断点然后继续向下走

        pos等于0时解决越界方法:

        解决方案一:把end设为为int类型,int类型的end与pos进行比较的时候,end会进行类型提高,成无符号类型,变成-1后依旧可以进行后移;所以比较时还要把pos的类型强转为无符号整形。

        解决方案二:不把当前数据向后移,而是把前一位数据移向当前位置,做法:把end定位在_size 的后一位,也就是“\0”的后一位,然后赋值移动,当pos == end的时候,就终止了,不会再--为-1, 循环时进行小于判断即可。修改关键代码如下:

//insert需要向后挪动数据
rsize_t end = _size + 1;
while (end > pos)
{_str[end] = _str[end - 1];end--;
}

        第二种解决方案更好。

(2)插入字符串

类比上面的逻辑得出:

void string::insert(size_t pos, const char* str)
{//扩容逻辑与append一样size_t len = strlen(str);if (_size + len > _capacity){size_t NewCapacity = _capacity * 2;if (_size + len > NewCapacity)//括两倍不够,则需要多少就括多少NewCapacity = _size + len;reserve(NewCapacity);}//insert需要向后挪动数据rsize_t end = _size + len;while (end != pos){_str[end] = _str[end - len];//把前len个数据向后移end--;}//插入数据for (size_t i = 0; i < len; i++){_str[pos + i] = str[i];//使用循环进行插入 }_size += len;
}

        这样可能会存在越界问题:当end的落地在第二个字符,而pos的位置是第一个字符,插入的字符串的len长度为6,就会造成越界访问。

        while的条件要改成 end > pos+len-1,因为当 end = pos + len时,pos = end - len,恰好是需要挪动的最后一位,然后end--,就是(pos + len)--,也就是pos + len - 1的时候就结束:

//insert需要向后挪动数据
size_t end = _size + len;
while ( end > pos + len - 1)
{_str[end] = _str[end - len];//把前len个数据向后移end--;
}

四、删除函数的实现

        在定义静态变量npos时,在类中声明,类外定义(static不用写);但在头文件中进行定义与声明时,又因为头文件会在其他文件在编译时展开,就会出现同一静态变量多次定义,造成连接错误。所以要在头文件中进行声明,在cpp文件中进行定义。

        const 静态变量npos就可以在类中进行声明与定义,是一个例外。但还是建议在类中声明,类外定义。

string.h文件

namespace zyb
{class string{public:static size_t npos;}
};

string.cpp文件

namespace zyb
{size_t string::npos = -1;
}

        删除函数的模拟实现:

string.h文件

void erase(size_t pos, size_t len = npos);

 string.cpp文件

void string::erase(size_t pos, size_t len)
{assert(pos < _size);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 = 0)
{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* str, size_t pos = 0)
{const char* ptr = strstr(_str + pos, str);if (ptr == nullptr)return npos;elsereturn (ptr - _str);
}

六、截取函数的实现

        截取函数的返回类型是string类型,涉及到拷贝构造的问题:浅拷贝与深拷贝

        截取函数的实现:

string string::substr(size_t pos, size_t len)
{assert(pos < _size);if (len > (_size - pos))len = _size - pos;//更新len的长度zyb::string sub;sub.reserve(len);for (size_t i = 0; i < len; i++){sub += _str[pos + i];}return sub;
}

        拷贝构造的实现:

string::string(const string& s)
{_str = new char[s._capacity + 1];strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;
}

七、运算符重载

        比较运算符重载到全局更好,因为可以进行string类与字符串的对比、字符串与string类的对比、string类与string类的对比,但重载到类中固定第一个操作数就是string类,没有重载到全局时的对比多。

        写的时候重载string类与string类的对比就行,其他的可以靠隐式类型转换来实现即可。

string.h文件

bool operator==(const string& lhs, const string& rhs);
bool operator!=(const string& lhs, const string& rhs);
bool operator>(const string& lhs, const string& rhs);
bool operator<(const string& lhs, const string& rhs);
bool operator>=(const string& lhs, const string& rhs);
bool operator<=(const string& lhs, const string& rhs);

string.cpp文件

//赋值运算符重载
string& string::operator=(const string& s)
{if (this != &s)//判断是不是自己给自己赋值{delete[] _str;_str = new char[s._capacity + 1];strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;}return *this;
}
bool operator==(const string& lhs, const string& rhs)
{return strcmp(lhs.c_str(), rhs.c_str()) == 0;
}
bool operator!=(const string& lhs, const string& rhs)
{return !(lhs == rhs);
}
bool operator>(const string& lhs, const string& rhs)
{return strcmp(lhs.c_str(), rhs.c_str()) > 0;
}
bool operator<(const string& lhs, const string& rhs)
{return strcmp(lhs.c_str(), rhs.c_str()) < 0;
}
bool operator>=(const string& lhs, const string& rhs)
{return !(lhs < rhs);
}
bool operator<=(const string& lhs, const string& rhs)
{return !(lhs > rhs);
}

        istream 和 ostream都不支持拷贝,所以在重载流输入或流输出时,iostream类型都要使用引用。

        cin和scanf都会忽略掉空格与换行,认为是多个值之间的分割。这样的话就拿不到输入的空格或换行。

        解决:io流是一个类的体系,cout和cin只是类中的一个对象,可以用其他接口来完成对输入空格与换行的读取。比如io流中的get函数,不会对空格或换行进行区分,无论是什么字符都可以输入:

cin.get();

        流输入与流提取的模拟实现如下:

string.h

iostream& operator<<(iostream& os, const string& s);//使用的是for循环来输出每一个字符
iostream& operator>>(iostream& is, string& s);

string.cpp文件

iostream& operator<<(iostream& os, const string& s)
{for (size_t i = 0; i < s.size(); i++){os << s[i];}return os;
}
iostream& operator>>(iostream& is, string& s)
{s.clean();char ch;ch = is.get();while (ch != ' ' && ch != '\n'){s += ch;ch = is.get();}return is;
}

注意:每次使用重载的流输入都要对其内容进行清除,不然会把新输入的东西放进旧字符串中。

void clean()
{_str[0] = '\0';_size = 0;
}

八、整体代码

string.h文件

#pragma once
#include<iostream>
#include<assert.h>
using namespace std;namespace zyb
{class string{public://一、构造函数// (一)无参构造//string();// (二)带参构造string(const char* str = "");//(三)拷贝构造string(const string& s);//二、析构函数~string();//三、返回字符串_str函数c_strconst char* c_str() const{return _str;}//四、遍历操作//(一)提供下标的最大值_sizesize_t size() const{return _size;}//(二)普通版本的operatorchar& operator[](size_t i){assert(i < _size);//判断是否在合理的范围之内return _str[i];}//(三)const版本的operatorconst char& operator[](size_t i) const{assert(i < _size);return _str[i];}//(四)迭代器using iterator = char*;using const_iterator = const char*;// 1、beginiterator begin(){return _str;// begin返回首元素的迭代器}// 2、beginiterator end(){return _str + _size;// end返回末尾元素的下一个位置的迭代器}// 3、const迭代器的beginconst_iterator begin() const{return _str;}// 4、const迭代器的beginconst_iterator end() const{return _str + _size;}//五、插入函数//容量控制void reserve(size_t n);//(一)push_backvoid push_back(char ch);//(二)appendvoid append(const char* str);//(三)operator+=string& operator+=(char ch);string& operator+=(const char* str);//六、插入与删除//(一)插入void insert(size_t pos, char ch);void insert(size_t pos, const char* str);//(二)删除void erase(size_t pos, size_t len = npos);//七、查找//(一)查找一个字符size_t find(char ch, size_t pos = 0);//(二)查找一个字符串size_t find(const char* str, size_t pos = 0);//八、截取string substr(size_t pos, size_t le = npos);//九、运算符重载string& operator=(const string& s);void clean(){_str[0] = '\0';_size = 0;}private:size_t _size;size_t _capacity;char* _str;public:static size_t npos;};bool operator==(const string& lhs, const string& rhs);bool operator!=(const string& lhs, const string& rhs);bool operator>(const string& lhs, const string& rhs);bool operator<(const string& lhs, const string& rhs);bool operator>=(const string& lhs, const string& rhs);bool operator<=(const string& lhs, const string& rhs);iostream& operator<<(iostream& os, const string& s);//使用的是for循环来输出每一个字符iostream& operator>>(iostream& is, string& s);}

string.cpp文件

#define _CRT_SECURE_NO_WARNINGS 1
#include"string.h"namespace zyb
{size_t string::npos = -1;//一、构造函数//(一)无参构造/*string::string():_str(new char[1] {'\0'}), _size(0), _capacity(0){}*///(二)带参构造string::string(const char* str):_size(strlen(str)){_capacity = _size;_str = new char[_size + 1];strcpy(_str, str);}//(三)拷贝构造string::string(const string& s){_str = new char[s._capacity + 1];strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;}//三、析构函数string::~string(){delete[] _str;_str = nullptr;//别忘了置空_size = 0;_capacity = 0;}//扩容void string::reserve(size_t n){if (n > _capacity){char* temp = new char[n + 1];//+1是给'\0'留位置strcpy(temp, _str);delete[] _str;_str = temp;_capacity = n;}}//五、插入函数//(一)push_backvoid string::push_back(char ch){if (_size == _capacity)//有效字符数与总容量相等就要进行扩容{reserve(_capacity == 0 ? 4 : _capacity * 2);//扩容,使用三目操作符,需要判断总空间是否为0的情况}_str[_size] = ch;_size++;}//(二)appendvoid string::append(const char* str){size_t len = strlen(str);if (_size + len > _capacity){size_t NewCapacity = _capacity * 2;if (_size + len > NewCapacity)//括两倍不够,则需要多少就括多少NewCapacity = _size + len;reserve(NewCapacity);}strcpy(_str + _size, str);_size += len;}//(三)operator+=string& string::operator+=(char ch){push_back(ch);return *this;}string& string::operator+=(const char* str){append(str);return *this;}//(四)insert//1、插入字符void string::insert(size_t pos, char ch){assert(pos <= _size);//扩容逻辑与push_back相同if (_size == _capacity)reserve(_capacity == 0 ? 4 : _capacity*2);//insert需要向后挪动数据rsize_t end = _size + 1;while (end > pos){_str[end] = _str[end - 1];//把前1个数据向后移end--;}//插入数据_str[pos] = ch;_size++;}//2、插入字符串void string::insert(size_t pos, const char* str){assert(pos <= _size);//扩容逻辑与append一样size_t len = strlen(str);if (_size + len > _capacity){size_t NewCapacity = _capacity * 2;if (_size + len > NewCapacity)//括两倍不够,则需要多少就括多少NewCapacity = _size + len;reserve(NewCapacity);}//insert需要向后挪动数据size_t end = _size + len;while ( end > pos + len - 1){_str[end] = _str[end - len];//把前len个数据向后移end--;}//插入数据for (size_t i = 0; i < len; i++){_str[pos + i] = str[i];//使用循环进行插入 }_size += len;}//六、删除函数void string::erase(size_t pos, size_t len){assert(pos < _size);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 = 0){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* str, size_t pos = 0){const char* ptr = strstr(_str + pos, str);if (ptr == nullptr)return npos;elsereturn (ptr - _str);}//八、截取string string::substr(size_t pos, size_t len){assert(pos < _size);if (len > (_size - pos))len = _size - pos;//更新len的长度zyb::string sub;sub.reserve(len);for (size_t i = 0; i < len; i++){sub += _str[pos + i];}return sub;}//九、运算符重载//赋值运算符重载string& string::operator=(const string& s){if (this != &s)//判断是不是自己给自己赋值{delete[] _str;_str = new char[s._capacity + 1];strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;}return *this;}bool operator==(const string& lhs, const string& rhs){return strcmp(lhs.c_str(), rhs.c_str()) == 0;}bool operator!=(const string& lhs, const string& rhs){return !(lhs == rhs);}bool operator>(const string& lhs, const string& rhs){return strcmp(lhs.c_str(), rhs.c_str()) > 0;}bool operator<(const string& lhs, const string& rhs){return strcmp(lhs.c_str(), rhs.c_str()) < 0;}bool operator>=(const string& lhs, const string& rhs){return !(lhs < rhs);}bool operator<=(const string& lhs, const string& rhs){return !(lhs > rhs);}iostream& operator<<(iostream& os, const string& s){for (size_t i = 0; i < s.size(); i++){os << s[i];}return os;}iostream& operator>>(iostream& is, string& s){s.clean();char ch;ch = is.get();while (ch != ' ' && ch != '\n'){s += ch;ch = is.get();}return is;}
}

        以上内容仅供分享,若有错误,请多指正

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

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

相关文章

Linux磁盘分区

文章目录 磁盘分区 &#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f916;Linux专栏&#xff1a;点击 ⏰️创作时间&#xff1a;2024年11月12日13点20分 磁盘分区 MBR 主启动记录分区方案指定了运行BIOS固件的系统上应如何对磁盘进行分区&#xff0c;存在与驱动开…

2. Spring Cloud 微服务基础环境搭建

2. Spring Cloud 微服务基础环境搭建 文章目录 2. Spring Cloud 微服务基础环境搭建前言1. 微服务需求解析2. 具体搭建微服务步骤&#xff1a;2.1 创建父工程 &#xff0c;用于聚合其它微服务模块2.1.1 需求说明/图解2.1.2 具体实现步骤2.1.3 注意事项和具体细节 2.2 创建会员中…

微信朋友圈营销

朋友圈营销4567法则

【赵渝强老师】MySQL InnoDB的表空间

InnoDB存储引擎目前是MySQL默认的存储引擎&#xff0c;它主要由三部分组成&#xff0c;分别是&#xff1a;存储结构、内存结构和线程结构。InnoDB的存储结构又可以分为逻辑存储结构和物理存储结构。InnoDB存储引擎的逻辑存储结构和Oracle大致相同&#xff0c;所有数据都被逻辑地…

docker安装redis

1、拉取镜像 docker pull redis:latest运行之前需要再/data/redis创建redis.conf配置文件 内容如下 # bind 192.168.1.100 10.0.0.1 # bind 127.0.0.1 ::1 #bind 127.0.0.1protected-mode noport 6379tcp-backlog 511requirepass roottimeout 0tcp-keepalive 300daemonize no…

vue项目多入口文件。vue.config.js如何修改配置

我们知道vue项目是单入口。指定一个入口文件去加载他所有的依赖。如果我们希望他有多个入口文件怎么办呢&#xff1f; 我们可以在public下面新建一个html的文件 然后src下新增一个文件夹&#xff0c;用来放APP.vue和 main.js。 然后修改vue.config.js。把他的pages改成2个入…

NCC前端调用查询弹框

系统自带的查询模板 弹框 调启使用默认的 查询模板 是在 单据模板的 列表模板中&#xff0c;有个查询区域 &#xff0c;查询区域就是查询模板内容如果在列表页做客开 新增按钮 调启查询模板 无问题&#xff0c;但是目前需求是需要再卡片页面下调启系统标准的调启模板代码 //调…

SpringBoot中的注解详解(二)

四、Param() &#xff08;mapper包 Dao层&#xff09; Param()&#xff1a; 功能&#xff1a; 用于在Mapper接口的方法参数上标记参数名称&#xff0c;以便在SQL语句中引用这些参数。 参数命名&#xff1a;在Mapper接口的方法参数上使用Param注解&#xff0c;可以为参数指定一…

一文1800字使用Jmeter进行http接口性能测试!

接口测试是测试系统组件间接口的一种测试。接口测试主要用于检测外部系统与系统之间以及内部各个子系统之间的交互点。测试的重点是要检查数据的交换&#xff0c;传递和控制管理过程&#xff0c;以及系统间的相互逻辑依赖关系等。 为什么要做接口测试&#xff1f; 越底层发现b…

新版flask pin码计算

Python debug pin码计算 需开启debug from flask import Flask app Flask(__name__) app.route("/") def index():return "Hello World" app.run(debugTrue) /console路由填入上方控制台的 PIN 码即可执行 Python 命令 Flask 的 PIN 码计算仅与 werkze…

比 PyTorch 更快的嵌入Python库:FastEmbed

嵌入生成 已成为自然语言处理&#xff08;NLP&#xff09;中不可或缺的一部分。 无论是智能推荐、文本相似度计算&#xff0c;还是聊天机器人&#xff0c;嵌入技术都扮演着重要角色。然而&#xff0c;我们常常会陷入繁重的库和庞大的模型中&#xff0c;耗时费力。 今天&#…

大模型部署解决方案之TorchServe+vLLM

TorchServe 是PyTorch 中将模型部署到生产环境的一个解决方案。它用HTTP 或HTTPS API 封装模型&#xff0c;可以处理多种任务&#xff0c;包括为部署模型分配workers、负责客户端和服务器之间通信等。 10月份发布的TorchServe 0.12 增加了对GenAI的支持&#xff0c;简化了大语…

博弈论(零和博弈)英文版题解

翻译&#xff1a; 假设我们有一个两人零和游戏&#xff0c;每个玩家有两种行动&#xff0c;行收益矩阵如下&#xff1a; 计算行和列玩家的最小最大最优策略以及游戏的价值。 X Y A a11 a12 B a21 a22 选项&#xff1a; 1. 行玩家&#x…

虚拟现实辅助工程技术应用于员工培训

你还在使用传统的入职方法吗&#xff0c;比如印刷指南、演示、课堂培训、讲座等等&#xff1f;是时候改变了。虚拟现实辅助工程技术提供了一个机会&#xff0c;可以让新员工的入职过程更高效、更有趣&#xff0c;也更令人兴奋。想象一下这样一个场景&#xff0c;新员工可以在第…

【健康警钟】胆已切除,生活调理有“胆”更精彩!必看指南!

在现代社会&#xff0c;由于生活习惯、饮食习惯等多种因素&#xff0c;一些人可能不得不面对胆囊切除手术。虽然手术能够有效解决胆囊结石、胆囊炎等问题&#xff0c;但胆囊作为人体的一部分&#xff0c;其功能的丧失无疑会对生活带来一定影响。那么&#xff0c;胆被割了之后&a…

windows NGIMX配置WebSocket反向代理

linux下 据说nginx是要有 stream的模块 Linux安装Nginx步骤之后续&#xff0c;带stream模块-CSDN博客 Nginx从1.3.13版本就开始支持WebSocket linux 下参考如下链接 配置 Nginx 反向代理 WebSocket - 哈喽哈喽111111 - 博客园 (cnblogs.com) SSL的配置参考 【Linux】采用…

三种读取配置文件的方式

在编写JDBC的util包以读取文件时&#xff0c;配置文件的位置会影响其读取方式。当前&#xff0c;默认配置文件直接放置在src文件夹下。 当读取.properties文件代码写法为&#xff1a; Properties props new Properties(); props.load(new FileInputStream("db.propertie…

丹摩征文活动|CogVideoX-2b:从安装到上线,轻松搞定全过程!

CogVideoX-2b&#xff1a;从安装到上线&#xff0c;轻松搞定全过程&#xff01; CogVideoX简介 CogVideoX的推出标志着视频生成技术的一次重大突破。过去&#xff0c;如何在保持高效的同时提升视频质量一直是一个难题&#xff0c;但CogVideoX 通过其先进的3D变分自编码器&…

工位管理优化:Spring Boot企业级系统

3系统分析 3.1可行性分析 通过对本企业级工位管理系统实行的目的初步调查和分析&#xff0c;提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、经济可行性、操作可行性等方面进行分析。 3.1.1技术可行性 本企业级工位管理系统采用SSM框架&#xff0c;JAVA作为开…

EMQX服务器的搭建,实现本地机和虚拟机之间的MQTT通信(详细教程)

前言 MQTT是一个基于客户端-服务器的消息发布/订阅传输协议。MQTT协议是轻量、简单、开放和易于实现的&#xff0c;这些特点使它适用范围非常广泛。 MQTT协议中有三种身份&#xff1a;发布者&#xff08;Publish&#xff09;、代理&#xff08;Broker&#xff09;&#xff08;…