当前位置: 首页 > news >正文

C++:模拟实现string

前言:

        为了更好的理解string底层的原理,我们将模拟实现string类中常用的函数接口。为了与std里的string进行区分,所以用命名空间来封装一个自己的strin类。

string.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
#include<assert.h>
#include<string>
using namespace std;
namespace manbo
{class string{public://迭代器typedef char* iterator;static size_t npos;//构造函数string(const char* s);//默认构造string();//拷贝构造string(const string& s);//析构函数~string();//返回字符串const char* c_str()const ;char& operator[](size_t pos);const char& operator[](size_t pos)const ;const size_t size()const;iterator begin(){return _str;}iterator end(){return _str + _size;}const iterator begin()const {return _str;}const iterator end()const {return _str + _size;}void reserve(size_t n = 0);void push_back(char ch);string& append(const char*s);string& operator+=(const char* s);string& operator+=(char ch);string& insert(size_t pos, const char* s);string& erase(size_t pos, size_t len = npos);size_t find(const char* s, size_t pos = 0) const;size_t find(char c, size_t pos = 0) const;string substr(size_t pos, size_t len =npos) const;void resize(size_t n, char ch = '\0');void clear();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 swap(string& s2);string& operator=(string temp);private:size_t _size;size_t _capaticy;char* _str;};ostream& operator<<(ostream& out, const string& s);istream& operator>>(istream& in, string& s);
}

        string里有三个私有成员变量 size,capacity,*str,分别表示字符数组的有效数据个数,容量,以及指向字符数组的指针。另外还有一个公有的char类型的指针iterator(迭代器)以及size_t类型的公有静态成员变量npos,并初始化值为-1实则会整型的最大值。

string.cpp

namespace manbo
{string::string(const char* s):_size(strlen(s)), _capaticy(_size), _str(new char[_size + 1]){memcpy(_str, s,_size+1);}string::string():_size(0), _capaticy(0), _str(new char[1]){_str[0] = '\0';}string::string(const string& s){_str = new char[s._size + 1];_size = s._size;_capaticy = s._capaticy;memcpy(_str,s._str,s._size+1);}string::~string(){delete[] _str;_str = nullptr;_size = _capaticy = 0;}const char* string::c_str()const {return _str;}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];}const size_t string ::size()const{return _size;}void string::reserve(size_t n){if (n>_capaticy){char* temp = new char[n + 1];memcpy(temp, _str,_size+1);_capaticy = n;delete[] _str;_str = temp;}}void string::push_back(char ch){if (_size==_capaticy){reserve(_capaticy == 0 ? 4 : 2 * _capaticy);}_str[_size++] = ch;_str[_size] = '\0';}string& string:: append(const char*s){size_t len = strlen(s);if (_size+len>_capaticy){reserve(2 * (_size + len));}memcpy(_str + _size,s,len+1);_size += len;return *this;}string& string::operator+=(const char* s){append(s);return *this;}string& string::operator+=(char ch){push_back(ch);return *this;}string& string::insert(size_t pos, const char* s){assert(pos >= 0 && pos <= _size);size_t n = strlen(s);size_t len = _size + n;_size += n;if (len>_capaticy){reserve(2 *len);}size_t end = _size;while (end>=pos&&end!=npos){_str[end + n] = _str[end];end--;}for (int i = 0; i <n ; i++){_str[pos + i] = s[i];}return *this;}string& string::erase(size_t pos, size_t len){assert(pos < _size);;if (len==npos||pos+len>=_size){_str[pos] = '\0';_size = pos;}else{int sum = 0;size_t begin = pos + len;while (begin<=_size){sum++;_str[pos] = _str[begin];pos++, begin++;}_size = pos + sum;}return *this;}size_t string::find(char c, size_t pos) const{for (size_t i = pos; i < _size; i++){if (_str[i] == c)return i;}return npos;}size_t string:: find(const char* s, size_t pos) const{char* temp = strstr(_str, s);if (!temp){return npos;}else{return temp-_str ;}}string string::substr(size_t pos, size_t len) const{assert(pos < _size);string temp;if (len == npos || pos + len >= _size){temp.reserve(_capaticy);for (size_t i = pos; i < _size; i++){temp += _str[i];}}else{temp.reserve(len + 1);for (size_t i = pos; i < len+pos; i++){temp += _str[i];}}return temp;}size_t string::npos = -1;void string::resize(size_t n, char ch){if (n<_size){_str[n] = '\0';_size = n;}else{reserve(n);for (int i = _size; i < n; i++){_str[i] = ch;}_str[n] = '\0';_size = n;}}void string::clear(){_str[0] = '\0';_size = 0;}// hello hello		false// hello*** hello	false// hello hello**	truebool string:: operator<(const string& s) const{size_t s1, s2;s1 = s2 = 0;while (s1<_size&&s2<s._size){if (_str[s1] < s._str[s2])return true;else if (_str[s1] > s._str[s2])return false;else{s1++, s2++;}}if (_size>s.size()){return false;}else if(_size == s.size()){return false;}return true;}bool string::operator==(const string& s) const{return _size == s._size && 0 == (memcmp(s._str, _str, _size));}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;}bool string::operator!=(const string& s) const{return !(*this == s);}void string:: swap( string& s){std::swap(_str, s._str);std::swap(_capaticy, s._capaticy);std::swap(_size, s._size);}//实现深拷贝string& string::operator=(string temp){swap(temp);return *this;}
}ostream& manbo:: operator<<(ostream& out, const string& s)
{for (auto ch:s){out << ch;}cout << endl;return out;
}
istream&manbo:: operator>>(istream& in, string& s)
{s.clear();char ch;ch = in.get();while (ch == '\0' || ch == '\n'){ch = in.get();}char buff[128];int i = 0;while (ch!='\0'&&ch!='\n'){buff[i++] = ch;if (i==127){buff[i] = '\0';s += buff;i = 0;}ch = in.get();}if (i>0){buff[i] = '\0';s += buff;}return in;
}

string::string(const char* s)

        以下是函数的解释:

        1.这是string类的构造函数,它接受一个const char*类型的参数s。这个参数代表一个 C语言 风格的字符串(即以 null 终止的字符数组)。

        2.初始化列表用于在构造函数体执行之前初始化类的成员变量。这里对三个成员变量进行了初始化:

                2.1strlen(s)计算字符串s的长度(不包括 null 终止符)。将这个长度赋值给_size成员变量,表示字符串的实际字符数。

                2.2_capaticy是字符串的容量。在这个实现中,容量被设置为与字符串长度相同,即_size

                2.3new char[_size + 1]分配了一块动态内存,大小为_size+1。这里+1是为了存储字符串的 null 终止符('\0')。将这块内存的指针赋值给_str,_str用于存储实际的字符串内容。

        3.构造函数体的内容是将源字符串 s 的内容复制到新分配的内存区域_str中。memcpy函数用于内存块的复制:复制从s开始的_size+1字节到_str。这里的_size+1是因为我们要复制整个字符串,包括 null 终止符。

    string::string()        

        以下是函数解释

        1.这是string类的默认构造函数。它不接受任何参数,并用于创建一个空的 string对象。    

        2.初始化列表用于在构造函数体执行之前初始化类的成员变量。这里对三个成员变量进行了初始化:

                2.1初始化_size成员变量为 0,表示字符串的长度为零。因为这是一个空字符串,所以长度为零。

                2.2初始化_capaticy成员变量为 0,表示字符串的容量也为零。

                2.3使用new char[1]分配了一块大小为 1 字节的动态内存。这块内存用于存储字符串及其终止符'\0'。

        

string::string(const string& s)

        以下是关于函数的解释

        这段代码是一个拷贝构造函数,用于创建一个string类的新对象,它是现有string对象的副本

  string::~string()      

        以下是函数的解释

        1.释放_str指针在堆上申请的空间,并将_str置为空指针

        2.将_size与_capaticy重新置为0;

const char* string::c_str()const 

         以下是函数的解释

        1.返回_str所指向的字符串。

char& string::operator[](size_t pos)

        以下是函数的解释

        1.这段代码定义了string类的一个成员函数operator[ ],用于访问字符串中的字符。这个函数是一个重载的下标运算符,允许通过下标直接访问字符串中的字符。

        2.assert是一个宏,用于在调试阶段检查条件是否为真。如果条件不为真,程序会中断并输出错误信息。这里检查pos是否在有效范围内,如果是则程序继续执行。

        3.return _str[pos];如果索引pos合法,函数返回_str[pos]。 _str是一个指向字符数组的指针,因此_str[pos] 表示数组中第 pos个位置的字符,并且返回的是引用所以可以对返回的值进行修改会影响到_str[pos]里的值。

const char& string::operator[](size_t pos)const

        以下是函数的解释

        与上个函数类似,但传入的对象以及返回的引用都被const进行修饰,此函数可以传const对象,因为这是一个权限的平移,而上个函数不能传const对象因为会产生权限的放大。并且对返回的引用只能进行读取而不能进行修改。

 const size_t string ::size()const   

   

        以下是函数的解释

        因为_size是成员变量默认是私有的所以不能直接返回,并且也不能随意修改,通过size() 函数来获取_size的值。

void string::reserve(size_t n)

                以下是函数的解释

                1.这段代码是一个成员函数reserve,用于调整string类的内部字符数组的容量,以便容纳至少n个字符的数据;

                2.判断n > _capaticy,检查n是否大于当前已分配的容量_capaticy,如果大于就扩容;

                3.new char[n + 1],在堆上分配一个新的字符数组,大小为n+1。因为额外的 1 个字符位置用于存储字符串的终止符'\0',并temp指针进行接收.

                4.memcpy(temp, _str, _size + 1)使用memcpy函数将旧字符数组_str的内容(包括终止符所以要+1)复制到新分配的内存temp中

                5.释放原_str的内容

                6.将_str重新指向新分配内存的指针temp的地址

void string::push_back(char ch)

         以下是函数的解释

        1.这个push_back函数用于向string对象的末尾添加一个字符,并处理容量的扩展。

        2.if (_size == _capaticy):检查当前字符串的大小是否等于当前容量。如果等于,说明字符串需要扩展容量,那么则调用创建好的reserve函数进行扩容,如果一开始是给空字符串那么_capacity会等于0,0乘任何数都得0,所以要加以进行判断。

        3.将字符ch添加到当前字符串的末尾,在赋值后递增_size,更新字符串的当前长度;

        4.在字符串末尾添加终止符'\0',标志着字符串的结束

    string& string:: append(const char*s)

        以下是关于函数的解释

        1.append函数用于将一个 C 风格的字符串(const char* s)追加到string对象的末尾

        2.strlen(s):计算要追加的字符串s的长度(不包括终止符 '\0'),并且赋值给len

        3.检查当前字符串的总长度(包括要追加的部分)是否超过当前容量,如果超过则调用reserve函数进行扩容

        4.拷贝字符串s从原string对象末尾('\0')的位置,并更新_size的值。

    string& string::operator+=(const char* s)

         以下是关于函数的解释

        operatir+=运算符重载,其参数为字符串,使用此operatir+=会复用append函数,在原字符串末尾追加字符串s,并返回*this(原对象)的引用。

        string& string::operator+=(char ch)

          以下是关于函数的解释

          operatir+=运算符重载,其参数为单个字符,使用此operatir+=会复用push_backd函数,在原字符串末尾追加字符ch,并返回*this(原对象)的引用。      

        string& string::insert(size_t pos, const char* s)

                以下是关于函数的解释

                1.insert函数用于在string对象中的指定位置插入字符串

                2.assert:检查插入位置pos是否在有效范围内(即不小于 0 且不大于当前字符串的_size)

                3.strlen(s)计算待插入的字符串s的长度,并将其赋值给n

                4.更新当前字符串的大小_size,将更新后的_size赋值给len,并判断是否需要扩容

                5.end:初始化为新的字符串长度_size,这是待会插入操作开始前的字符串末尾。        

                6.while循环:从字符串的末尾向插入位置pos移动字符,为腾出空间插入新字符串,_str[end + n] = _str[end]:将字符移动到新的位置并将end--.。注意,npos是一个静态成员变量,通常定义为size_t(-1),表示无效位置。在这里,它的作用是防止end变成负值,从而避免无限循环。

                7.for循环:将待插入的字符串s 的字符逐个复制到目标位置pos开始的位置

                8.返回 *this(当前对象)的引用,以支持链式调用。

string& string::erase(size_t pos, size_t len)

        以下是关于函数的介绍

        1.erase函数用于从string对象中删除指定位置的字符,删除的长度由 len 参数指定(缺省值为npos)。它的实现包括了处理不同情况的逻辑,以确保删除操作正确地更新字符串的内容和大小。

        2.assert:检查pos 是否在有效范围内(即 pos 应小于当前字符串的大小_size)

        3.if语句:处理特殊情况

                3.1如果len的值是npos(表示删除到字符串的末尾)或者pos + len超过了 _size,则删除从pos位置到字符串末尾的所有字符。

                3.2_str[pos] = '\0';:将删除位置设置为字符串结束符'\0',这会将字符串从pos位置截断,并更新字符串的大小 _size,以表示新的字符串长度。

        4.else语句:处理len小于字符串剩余长度的情况

                4.1 int sum = 0;:初始化一个计数器sum,用于记录实际移动的字符数。

                4.2 size_t begin = pos + len;:确定开始移动的字符位置。

                4.3 while (begin <= _size)遍历从begin到字符串末尾的所有字符,并将它们向前移动到删除的区域(这里字符'\0'也会向前移动,所以不需要再添加'\0'),以覆盖掉删除的字符。

        5._size = pos + sum;:更新字符串的大小_size,它应该等于新末尾的位置加上实际移动的字符数。

    

size_t string::find(char c, size_t pos) const

        以下是关于函数的介绍

        通过遍历来查找传入的字符c,如果找到就返回下标,如果没找到则返回npos。

size_t string:: find(const char* s, size_t pos) const

        以下是关于函数的介绍

        1.运用c语言的库函数strstr查找子串并用char类型的指针temp进行接受

        2.如果temp是NULL那么就没找到返回npos,如果找到了则将temp的地址-_str的首地址,最终会算出子串的第一个字符的下标位置。

    string string::substr(size_t pos, size_t len) const

                

        以下是关于函数的介绍

        1.substr函数用于生成当前字符串对象的子字符串。它的实现包括对子字符串的提取和处理

        2.assert(pos < _size);确保pos 在有效范围内。这里_size表示当前字符串的实际大小

        3.string temp;创建一个名为 temp的 string 对象,用于存储提取的子字符串。

        4.if (len == npos || pos + len >= _size),如果len是npos(表示提取从pos到字符串末尾)或者pos + len 超过了当前字符串的大小 _size,则处理这两种情况:

                4.1预先为temp对象开好_capaticy个空间,目的是避免频繁的扩容

                4.2 for循环,从pos位置开始遍历到字符串末尾,将每个字符添加到temp字符串中。

        5 当len小于等于字符串的剩余长度时,提取从pos开始的长度为len的子字符串

                5.1为temp字符串扩容,len+1是为了确保有足够的空间来存储len个字符和一个'\0'

                5.2 for循环从pos位置开始,遍历len个字符,将每个字符添加到 temp 字符串中。

注意:这里并不需要专门再结尾添加'\0',因为temp +=会去调用operator+=的运算符重载,而operator+=里又会调用push_back函数,再push_back函数里会对字符末尾进行添加'\0'的操作

void string::resize(size_t n, char ch)

        以下是关于函数的解释

        1.resize函数用于调整string对象的大小,并且可以用特定的字符填充新增的部分。

        2.如果传入的n是小于_size的那么直接将字符串_str[n]替换为'\0',并将_size更新为n;

        3.如果是n大于_size那么先将string对象的内存进行扩容,确保字符串的存储空间足够,然后从_size位置到n 位置填充字符ch。接着在_str[n]插入终结符 '\0',并更新_size为n。

void string::clear()

         以下是关于函数的解释

        clear顾名思义是清理的意思,那么直接在_str[0]的位置插入终止符'\0',并把_size更新为0,这里没有将_capacity置空是为了怕对象还要进行插入数据,防止多次扩容消耗性能。

bool string:: operator<(const string& s) const

        以下是关于函数的解释

        .operator<函数定义了一个 <操作符,用于比较当前 string 对象与另一个 string对象的大小。它主要用于ASCII比较(即按字母顺序比较)

        

其他的operator比较符重载

string& string::operator=(string temp)

        以下是关于函数的解释  

        1.接受一个string对象temp的形参

        2.在函数体内调用swap(temp),这将当前对象的内容与temp对象的内容交换。

        3.交换后,当前对象就变成了 temp 的内容,temp变成了当前对象的旧内容,并且因为temp是局部参数,在函数调用的时候自动进行析构,防止了内存泄漏

ostream& manbo:: operator<<(ostream& out, const string& s)

        以下是关于函数的解释

        1.operator<<函数是一个重载的输出流操作符,用于将string对象的内容输出到输出流

        2.范围for循环,auto ch自动推导ch的类型为s中的字符类型,循环遍历s中的每一个字符,并将其逐个输出到流out中

        3.返回流out以支持链式操作。比如:out<<s1<<s2;

istream&manbo:: operator>>(istream& in, string& s)

        以下是关于函数的解释

        1.operator>>函数是一个重载的输入流操作符,用于从输入流读取字符串,并将其存储到string对象s中。

        2.s.clear();使s变为空字符串,以确保读取的内容覆盖之前的内容.

        3.char ch;ch = in.get();使用in.get()从流中读取一个字符并存储在ch中。

        4.while循环跳过开头无用的字符如'\0'或'\n',直到读取到第一个有用的字符

        5.使用一个字符数组buff作为缓冲区来临时存储字符,i用于确认buff数组的位置。

        6.while循环, 只要字符不是'\0'和'\n',就将字符存入缓冲区buff。缓冲区buff里有 127 个字符时(留一个位置给终止符'\0'),将缓冲区内容+=到s中,并重置i。

        7.如果i>0表示buff里有剩余的字符,将这些字符+=到s对象中

        8. 返回流in的引用,以支持链式输入操作。比如:cin>>s1>>s2;

        

测试文件:

void test01()
{manbo::string s1;manbo::string s2="Hello world";cout << s2.c_str() << endl;cout << s1.c_str() << endl;manbo::string const s3 = "asdddd";
}void test02()
{manbo::string const s2 = "Hello world";manbo::string::iterator it = s2.begin();while (it!=s2.end()){cout << *it;it++;}cout << endl;
}void test03()
{manbo::string  s2 = "Hello world";s2.insert(6, "******");cout << s2.c_str() << endl;s2.erase(0);cout << s2.c_str() << endl;
}void test04()
{manbo::string  s2 = "Hello world";size_t pos1 = s2.find("world");size_t pos2 = s2.find('e');cout << pos2 << endl;
}void test05()
{manbo::string s1 = "https://www.bilibili.com/video/BV1fW421X7gD/?spm_id_from=333.1007.tianma.6-1-19.click";//解析网址,分协议   域名  资源size_t pos1 = s1.find("://");if (pos1 == manbo::string::npos){cout << "No Find";exit(1);}manbo::string agreement = s1.substr(0, pos1);cout << "协议:" << agreement.c_str()<< endl;size_t pos2 = s1.find('/', pos1 + 3);if (pos2 == manbo::string::npos){cout << "No Find";exit(1);}manbo::string domain = s1.substr(pos1 + 3, pos2 - (pos1 + 3));cout << "域名:" << domain.c_str()<< endl;manbo::string resource = s1.substr(pos2 + 1);cout << "资源:" << resource.c_str() << endl;
}void test06()
{string s1 = "hello world";manbo::string s2 = "hello world";//s1.resize(4,'x');//s2.resize(4,'x');s1 += '\0';s1 += "******";s2 += '\0';s2 += "******";cout << s1 << endl;cout << s2<< endl;manbo::string s3;cin >> s3;cout << s3;cin >> s3;cout << s3;
}void test07()
{manbo::string s1="hello";manbo::string s2="hello world";cout << (s1 < s2) << endl;cout << (s1 == s2) << endl;cout << (s1 > s2) << endl << endl;manbo::string s3 = "hello world";manbo::string s4 = "hello";cout << (s3 < s4) << endl;cout << (s3 == s4) << endl;cout << (s3 > s4) << endl<<endl;manbo::string s5 = "hello";manbo::string s6 = "hello";cout << (s5 < s6) << endl;cout << (s5 == s6) << endl;cout << (s5 > s6) << endl << endl;
}void test08()
{manbo::string s1 = "hello";manbo::string s2 = "hello world";s1 = s2;cout << s1;}

相关文章:

C++:模拟实现string

前言&#xff1a; 为了更好的理解string底层的原理&#xff0c;我们将模拟实现string类中常用的函数接口。为了与std里的string进行区分&#xff0c;所以用命名空间来封装一个自己的strin类。 string.h #pragma once #define _CRT_SECURE_NO_WARNINGS 1#include<iostream&…...

浅谈Kafka(一)

浅谈Kafka&#xff08;一&#xff09; 文章目录 浅谈Kafka&#xff08;一&#xff09;Kafa的设计是什么样的数据传输的事务定义消息队列的应用场景Kafka怎么样判断节点是否存活Kafka的消息是采用pull模式还是push模式Kafka在磁盘上的消息格式Kafka高效文件存储设计特点Kafka与传…...

Redis7基础篇(八)

redis集群 是什么 能干吗 集群算法-分片-槽位slot redis集群的槽位slot redis集群的分片 分片和槽位的优势 槽位映射的解决方案 上面的三个方案分别对应了小厂 中厂 大厂 哈希槽取余分区 缺点 一致性哈希算法分区 小总结 哈希槽分区 经典面试题 这里说的redis是ap而不是cp的 …...

Tauri简介

在Tauri应用中&#xff0c;Rust和前端&#xff08;通常是基于Web技术如React、Vue或Angular&#xff09;之间的交互是一个核心特性&#xff0c;它允许开发者利用Rust的强大功能和性能&#xff0c;同时保持前端开发的灵活性和丰富的生态系统。这种交互主要通过Tauri提供的API桥接…...

JavaWeb——MVC架构模式

一、概述: MVC(Model View Controller)是软件工程中的一种 软件架构模式 &#xff0c;它把软件系统分为模型、视图和控制器三个基本部分。用一种业务逻辑、数据、界面显示分离的方法组织代码&#xff0c;将业务逻辑聚集到一个部件里面&#xff0c;在改进和个性化定制界面及用户…...

Excel求和方法之

一 SUM&#xff08;&#xff09;&#xff0c;选择要相加的数,回车即可 二 上面的方法还不够快。用下面这个 就成功了 三 还有一种一样快的 选中之后&#xff0c;按下Alt键和键&#xff08;即Alt&#xff09;...

Windows Server 域控制服务器安装及相关使用

目录 1.将客户机加入域 2.安装域控制器 3.新建域用户 4.设置用户登录时间&#xff0c;账户过期时间 5.软件分发 ​编辑 6.换壁纸 7.OU与GPO的概念 域为集中控制&#xff0c;拿下域控是拿下目标的关键 以Windows Server 2022为例 1.将客户机加入域 前提&#xff1a;客…...

linux基础命令(超级详细)

Linux 系统提供了丰富的命令行工具&#xff0c;用于各种文件操作、系统管理和网络配置等任务。以下是一些常用的 Linux 基础命令&#xff1a; 一、 文件和目录操作 1. ls: 列出目录内容 ls 列出当前目录的文件和目录 ls -l 以长格式列出文件和目录&#xff0c;包…...

大模型笔记之-XTuner微调个人小助手认知

前言 使用XTuner 微调个人小助手认知 一、下载模型 #安装魔搭依赖包 pip install modelscope新建download.py内容如下 其中Shanghai_AI_Laboratory/internlm2-chat-1_8b是魔搭对应的模型ID cache_dir/home/aistudio/data/model’为指定下载到本地的目录 from modelscope im…...

用TensorFlow实现线性回归

说明 本文采用TensorFlow框架进行讲解&#xff0c;虽然之前的文章都采用mxnet&#xff0c;但是我发现tensorflow提供了免费的gpu可供使用&#xff0c;所以果断开始改为tensorflow&#xff0c;若要实现文章代码&#xff0c;可以使用colaboratory进行运行&#xff0c;当然&#…...

IT计算机软件系统类毕业论文结构指南:从标题到结论的全景视角

一、背景 在快速发展的IT和人工智能领域&#xff0c;毕业论文不仅是学术研究的重要成果&#xff0c;也展示了学生掌握新技术和应用的能力。随着大数据和智能系统的复杂性增加&#xff0c;毕业设计&#xff08;毕设&#xff09;的论文章节安排变得尤为关键。一个结构清晰、内容详…...

leetcode27:移除元素(正解)

移除元素 给你一个数组 nums 和一个值 val&#xff0c;你需要 原地 移除所有数值等于 val 的元素。元素的顺序可能发生改变。然后返回 nums 中与 val 不同的元素的数量。 假设 nums 中不等于 val 的元素数量为 k&#xff0c;要通过此题&#xff0c;您需要执行以下操作&#xf…...

docker部署nginx--(部署静态文件和服务)

文档参考 1、http://testingpai.com/article/1649671014266 2、下载nginx docker pull nginx:alpine 然后启动nginx&#xff0c; docker run --rm -it -p 9192:80 nginx:alpine /bin/sh 关闭容器后&#xff0c;自动删除该容器 进入后&#xff0c;启动nginx, nginx进行curl h…...

websocket的介绍及springBoot集成示例

目录 一、什么是Websocket 二、Websocket特点 三、WebSocket与HTTP的区别 四、常见应用场景 五、SpringBoot集成WebSocket 1. 原生注解 2. Spring封装 一、什么是Websocket WebSocket 是一种在单个 TCP 连接上进行 全双工 通信的协议&#xff0c;它可以让客户端和服务器…...

软件测试-自动化测试

自动化测试 测试人员编写自动化测试脚本&#xff0c;维护并解决自动化脚本问题 自动化的主要目的就是用来进行回归测试 回归测试 常见面试题 ⾃动化测试能够取代人工测试吗&#xff1f; ⾃动化测试不⼀定⽐人工测试更能保障系统的可靠性&#xff0c;⾃动化测试是测试⼈员手…...

Linux 安装TELEPORT堡垒机

一、查看官方文档 堡垒机官网地址&#xff1a;走向成功 - Teleport&#xff0c;高效易用的堡垒机 &#xff08;一&#xff09;官网资源链接 -》Teleport 在线文档 &#xff08;二&#xff09;手动下载安装包 二、压缩包下载和安装 &#xff08;一&#xff09;加压下载的安装…...

【14】即时编译器的中间表达形式

中间表达形式&#xff08;IR&#xff09; 编译器一般被分为前端和后端。 前端会对输入的程序进行词法分析、语法分析和语义分析&#xff0c;然后生成中间表达形式&#xff08;IR&#xff09;&#xff1b;后端对IR进行优化&#xff0c;生成目标代码 不考虑解释执行的话&#xf…...

Mysql(三)---增删查改(基础)

文章目录 前言1.补充1.修改表名1.2.修改列名1.3.修改列类型1.4.增加新列1.5.删除指定列 2.CRUD3.新增(Create)3.1.单行插入3.2.指定列插入3.3.多行插入 4.数据库的约束4.1.约束的分类4.2.NULL约束4.3.Unique约束4.4.Default 默认值约束4.5.PRIMARY KEY&#xff1a;主键约束4.6.…...

Dialog实现原理分析

在 Android 中&#xff0c;对话框&#xff08;Dialog&#xff09;是一种非常常见的用户界面组件&#xff0c;用于向用户提供额外的信息或者请求用户的确认。Android 提供了几种不同类型的对话框&#xff0c;例如简单的消息对话框 (AlertDialog)、进度条对话框 (ProgressDialog)…...

21.1 基于Netty实现聊天

21.1 基于Netty实现聊天 一. 章节概述二. `Netty`介绍三. 阻塞与非阻塞1. 阻塞与非阻塞简介2. BIO同步阻塞3. NIO同步非阻塞4. AIO异步非阻塞IO5. 异步阻塞IO(用的极少)6. 总结四. Netty三种线程模型1. 单线程模型2. 多线程模型3. 主从线程模型五. 构建Netty服务器************…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具

文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...

剑指offer20_链表中环的入口节点

链表中环的入口节点 给定一个链表&#xff0c;若其中包含环&#xff0c;则输出环的入口节点。 若其中不包含环&#xff0c;则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...

解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错

出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上&#xff0c;所以报错&#xff0c;到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本&#xff0c;cu、torch、cp 的版本一定要对…...

selenium学习实战【Python爬虫】

selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...

云原生玩法三问:构建自定义开发环境

云原生玩法三问&#xff1a;构建自定义开发环境 引言 临时运维一个古董项目&#xff0c;无文档&#xff0c;无环境&#xff0c;无交接人&#xff0c;俗称三无。 运行设备的环境老&#xff0c;本地环境版本高&#xff0c;ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...

tomcat指定使用的jdk版本

说明 有时候需要对tomcat配置指定的jdk版本号&#xff0c;此时&#xff0c;我们可以通过以下方式进行配置 设置方式 找到tomcat的bin目录中的setclasspath.bat。如果是linux系统则是setclasspath.sh set JAVA_HOMEC:\Program Files\Java\jdk8 set JRE_HOMEC:\Program Files…...

渗透实战PortSwigger靶场:lab13存储型DOM XSS详解

进来是需要留言的&#xff0c;先用做简单的 html 标签测试 发现面的</h1>不见了 数据包中找到了一个loadCommentsWithVulnerableEscapeHtml.js 他是把用户输入的<>进行 html 编码&#xff0c;输入的<>当成字符串处理回显到页面中&#xff0c;看来只是把用户输…...

Java中HashMap底层原理深度解析:从数据结构到红黑树优化

一、HashMap概述与核心特性 HashMap作为Java集合框架中最常用的数据结构之一&#xff0c;是基于哈希表的Map接口非同步实现。它允许使用null键和null值&#xff08;但只能有一个null键&#xff09;&#xff0c;并且不保证映射顺序的恒久不变。与Hashtable相比&#xff0c;Hash…...

RabbitMQ 各类交换机

为什么要用交换机&#xff1f; 交换机用来路由消息。如果直发队列&#xff0c;这个消息就被处理消失了&#xff0c;那别的队列也需要这个消息怎么办&#xff1f;那就要用到交换机 交换机类型 1&#xff0c;fanout&#xff1a;广播 特点 广播所有消息​​&#xff1a;将消息…...

云原生时代的系统设计:架构转型的战略支点

&#x1f4dd;个人主页&#x1f339;&#xff1a;一ge科研小菜鸡-CSDN博客 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; 一、云原生的崛起&#xff1a;技术趋势与现实需求的交汇 随着企业业务的互联网化、全球化、智能化持续加深&#xff0c;传统的 I…...