详解string类+迭代器
迭代器
概念:在 C++ 中,迭代器是访问容器(如数组、列表、向量、字符串等)元素的一种方式。迭代器提供了一种统一的接口,使得你可以使用相同的代码来遍历不同类型的容器。迭代器本质上是一个指针或者指针的封装,它指向容器中的某个元素。
iterator:表示迭代器类型,通常用于声明迭代器变量
迭代器是通用的,能更好与算法配合
迭代器的分类
在C++中,迭代器根据功能和能力被分为 5种类型,支持的操作逐渐增强。
以 std::string 为例,它的迭代器属于 随机访问迭代器。
获取迭代器
迭代器操作
范围for循环
一个类只要支持迭代器就支持范围for,其底层实现就是迭代器。范围for存在局限性,只能顺着遍历。若是自定义类需要自己实现普通和const迭代器。
aoto关键字
核心作用:让编译器自动推导变量类型,避免显式写出冗长或复杂的类型名
推导规则:
1.忽略顶层 const 和引用,保留底层const:
什么是顶层引用,为什么忽略?
C++ 中并没有“顶层引用”这一标准术语,但可以通过对比 “顶层 const” 来类比理解:
1.顶层 const:表示对象本身是常量(直接修饰变量本身)。例如const int a = 10; 中的 a 是顶层 const。
底层 const:表示指针或引用指向的对象是常量。例如:const int* p = &a; 中的 p 是底层 const(指针指向的内容不可变)。
2.避免意外的引用绑定:如果 auto 自动保留引用,可能导致未预期的副作用(例如无意中修改原数据)。
简化代码逻辑:大多数情况下,开发者可能只需要操作副本而非用。
使用规则:
auto 会丢弃初始化表达式中的引用属性,推导出被引用对象的类型。若需要保留引用,必须显式添加 &。
2.数组退化为指针
注意事项:
1.必须初始化。auto 变量必须显式初始化,否则无法推导类型
2.类型可能不符合预期。推导结果可能因初始化表达式不同而变化
3.谨慎使用引用和 const。需显式指定 & 或 const 以保留引用或常量性
auto范围for的使用场景
auto加引用,ch 是字符串中字符的引用,直接绑定到原字符串的每个字符上。在循环体内对 ch 的修改(如 ch++)会直接影响原字符串。
不加引用auto ch,ch 是字符串中字符的副本,与原字符串完全独立。
对 ch 的修改(如 ch++)仅作用于副本,不会影响原字符串。原字符串保持不变。
1.为什么学习string类
C语言中,字符串是以’\0’结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,不太符合OOP(面向对象编程)的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。
所以C++中专门在标准模板库中封装了一个string类,方便我们进行有关字符串的操作,在实现时不用自己再实现代码细节,只需正确调用函数即可
2.标准库中的string类
2.1了解string类
定义:std::string 是一个模板类,定义在 头文件中。它本质上是一个动态数组,用于存储字符序列。
- 字符串是表示字符序列的类
- 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作单字节字符字符串的设计特性。
- string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型(关于模板的更多信息,请参阅basic_string)
- string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_traits和allocator作basic_string的默认参数(更多的模板信息请参考basic_string)。
- 注意,这个类独立于所使用的编码来处理字节:如果用来处理多字节或变长字符(如UTF-8)的序列,这个类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。不能操作多字节或者变长字符的序列。
在使用string类时,必须包含#include头文件以及using namespace std。
2.2常用接口说明
构造函数
1.默认构造函数:创建一个空对象,不需要任何参数。
2.复制构造函数:通过已有的string对象来创建一个新的string对象,参数是对一个string对象的常量引用。
3.从子字符串构造:第一个参数是对原始字符串的引用,pos是子字符串开始的位置,len是子字符串的长度。
4.从C字符串构造:参数是一个指向字符数组的指针。
5.从字符序列构造:从给定的字符数组中复制前n个字符来创建对象,参数是一个指向字符数组的指针和要复制的字符数量。
6.填充构造函数:参数是字符串长度和填充字符
7.模板构造函数:允许使用任何类型的输入迭代器来构造一个字符串。参数确定序列的范围。
容量操作函数
size:返回字符串有效字符长度
与length底层实现原理一样,引入size是为了与其他接口保持一致,一般情况下都用size()。和strlen一样不计入’\0’
capacity:返回当前分配的存储空间大小,即字符串的容量。
empty:检查字符串是否为空。返回 true;否则返回 false
clear:将s中的字符串清空
注意清空时只是将size清0,不改变底层空间的大小
reserve:请求改变字符串的容量,只开空间。
如果 n 大于当前容量,字符串的容量会被增加到至少 n;如果 n 小于或等于当前容量,此调用可能不会改变容量,这是一个不具有约束力的请求。底层实现看是否还存有数据(是否使用clear+reserve(0)),若有则不缩,反之缩。
一般情况下不会缩容,因为系统不支持分段释放内存,缩容是以时间换空间的方式 ,将原空间需保留部分拷贝到新空间,再释放原空间。
n 是一个 size_t 类型的参数,表示要预留的最小字符数。如果省略此参数或其值为 0,则不会预留额外的内存。通过预留足够的内存,可以减少在字符串增长过程中因内存不足而进行的多次内存分配和数据复制,从而提高程序的性能
resize:预留足够的内存,开空间+填值初始化。
如果 n 小于当前长度,字符串会被截断;如果 n 大于当前长度,字符串会被扩展,并用空字符填充。
存在第二种带填充字符的重载。将字符串的大小调整为 n 个字符,如果需要扩展字符串,则使用字符 c 进行填充。如果当前字符串长度大于 n,则截断字符串,只保留前 n 个字符。
注意:resize 函数不会抛出异常,即使在内存分配失败的情况下,它也会静默地减少字符串的大小。
max_size:返回可容纳的最大字符数
由 std::string 所基于的 std::allocator 决定的,并且受到系统内存限制的影响。
类对象访问及遍历操作
是一个重载的运算符函数,它允许你使用方括号[]来访问字符串中的字符,会进行边界检查,operator定义中会先断言<_size。这个运算符有两个版本:char& operator[] (size_t pos):
这是一个非常量版本,返回一个对字符串中指定位置pos的字符的引用。这意味着你可以通过这个引用来修改字符串中的字符。const char& operator[] (size_t pos) const:
这是一个常量版本,返回一个对字符串中指定位置pos的字符的常量引用。这意味着你可以读取字符串中的字符,但不能通过这个引用来修改它。
对比at,at是在还没[]运算符重载时出现的,同样具有查找和修改的功能。
区别于[],at会进行边界检查,通常慢一点,当出现边界问题时会抛异常。[]不会检查边界,出问题直接报错。at提供了更好的安全性和错误处理能力,可以帮助避免潜在的运行时错误。但在日常中索引值一般都确定,更加习惯于使用[]。
都返回一个迭代器
begin:指向容器的第一个元素
end:指向容器“结束”的位置,即最后一个元素之后的位置
rbegin:指向容器的最后一个元素,即反向遍历的开始位置
rend:指向容器第一个元素之前的位置,即反向遍历结束的位置
cbegin:返回一个常量迭代器,指向容器第一个元素,常量迭代器不允许修改容器中的元素
cend:返回一个常量迭代器,指向容器的“结束”位置。
crbegin:返回一个常量反向迭代器(const_reverse_iterator),其余和rbegin相同
crend:返回一个常量反向迭代器,其余和rend相同
string的三种遍历方式
void Teststring3()
{string s("hello Bit");// 3种遍历方式:// 需要注意的以下三种方式除了遍历string对象,还可以遍历是修改string中的字符,// 另外以下三种方式对于string而言,第一种使用最多// 1. for+operator[]for (size_t i = 0; i < s.size(); ++i)cout << s[i] << " ";cout << endl;// 2.迭代器string::iterator it = s.begin();while (it != s.end()){cout << *it <<" ";++it;}cout << endl;//string::reverse_iterator rit = s.rbegin();//C++11之后,直接使用auto定义迭代器,让编译器推到迭代器的类型auto rit = s.rbegin();while (rit != s.rend()){cout << *rit << " ";rit++;}cout << endl;// 3.范围forfor (auto& ch : s){cout << ch << " ";ch++;}cout << endl;
}
int main()
{Teststring3();return 0;
}
string类对象的修改操作
1.operator+=:重载的加法赋值运算符,用于将另一个字符串或字符串字面量追加到当前字符串的末尾。
2.append:将指定的字符串或字符数组追加到当前字符串的末尾
3.push_back:将单个字符追加到字符串的末尾
4.assign:将字符串的内容替换为指定的字符串或字符数组,会覆盖
5.insert:在字符串的指定位置插入另一个字符串或字符数组。头部插入存在数据挪动导致效率问题
6.erase:从字符串中删除指定范围的字符
7.replace:替换字符串中指定范围的字符为另一个字符串或字符数组
8.swap:交换当前字符串与另一个字符串的内容
9.pop_back删除字符串最后一个字符
各接口的具体实现细节可以在cplusplus.com官网上去查看。这里不一一列举,学习了解重点即可。
字符串操作
c_str() 函数用于获取一个 C++ 风格的字符串(C string),也就是以空字符(‘\0’)结尾的字符数组。返回一个指向内部数据的指针,该数据是一个以空字符终止的字符数组,即 C 风格的字符串。这个指针可以用来与需要 C 风格字符串的函数或库进行交互,使得可以在需要 C 风格字符串的上下文中使用 C++ 字符串。
重点分析:
1.查找字符串,pos参数指定开始查找位置,默认为0
2.查找C风格字符串
3.查找字符数组,pos为开始位置,n为要查找的字符数
4.查找单个字符,pos为开始位置默认为0,可以指定很方便
未找到都返回std::string::npos
用于从一个字符串对象中提取子字符串,并返回一个新的字符串对象,原始字符串不会被修改。
pos为开始查找位置,默认为0,len表示要提取的字串长度,默认为npos,是一个很大的值,若不自己给范围将提取到字符串末尾。
如果指定的 pos 超出了主字符串的有效范围,或者 len 为 0,则 substr 函数将返回一个空字符串。
这两个接口函数可以很好的实现查找和分割字符串,以获取一个网址的协议 域名 资源名为例。
string结构的说明
下述结构是在32位平台下进行验证,32位平台下指针占4个字节.
vs下string的结构
string总共占28个字节,内部结构稍微复杂一点,先是有一个联合体,联合体用来定义string中字符串的存储空间:
当字符串长度小于16时,使用内部固定的字符数组来存放
当字符串长度大于等于16时,从堆上开辟空间
这种设计也是有一定道理的,大多数情况下字符串的长度都小于16,那string对象创建好之后,内部已经有了16个字符数组的固定空间,不需要通过堆创建,效率高。其次:还有一个size_t字段保存字符串长度,一个size_t字段保存从堆上开辟空间总的容量最后:还有一个指针做一些其他事情。故总共占16+4+4+4=28个字节。
g++下string的结构
G++下,string是通过写时拷贝实现的,string对象总共占4个字节,内部只包含了一个指针,该指针将来指向一块堆空间,内部包含了如下字段:
空间总大小
字符串有效长度
引用计数
指向堆空间的指针,用来存储字符串
2.3写时拷贝(了解)
写时拷贝是为了解决有时深拷贝代价过大问题出现的(比如深拷贝后什么都不操作,,浪费空间资源)。所以拷贝时会先进行浅拷贝,但浅拷贝存在两个问题:
1.析构两次
因此引入引用计数的概念,引用计数是为了记录当前有多少个对象在使用同一块空间,当一个对象调用析构函数被销毁时,引用计数-1,再看其是否为0,为0就代表没有其他对象在使用这块空间,可以进行资源释放,若引用计数不为0,就不释放,解决了问题1.
2.一个对象修改会影响另一个
当对对象进行修改等操作时,若引用计数不为1,代表有多个对象使用同一块空间,则要进行深拷贝,再修改。这样子修改时先检查引用计数,若等于0就不用再进行固定的深拷贝浪费空间资源,以加入引用计数较小的代价,有时可以避免深拷贝较大的代价,可谓是赚到了。解决了问题2.
图中实现了写时拷贝,g++下。
不同平台下底层实现不同,如vs下就没有
vs采取另一种方式来存储,以空间换时间,当字符串过大时,存在一定空间浪费
在后续智能指针和Linux的学习中,会继续深入学习写时拷贝。
3.string类的模拟实现
实现细节已在代码中注释,主要实现常用接口以及相关操作
- string.h
#pragma once
#include<assert.h>
using namespace std;namespace ee
{class string{friend ostream& operator<<(ostream& out, const ee::string& s);friend istream& operator>>(istream& in, ee::string& s);public:typedef char* iterator;typedef const char* const_iterator;iterator begin(){//_str为指向字符串首元素地址的指针return _str;}iterator end(){//_size隐式转换成指针类型return _size+_str;}const_iterator begin() const{return _str;}const_iterator end() const{return _str + _size;}//构造函数,从C风格字符串创建string(const char* str=""){_size = strlen(str);_capacity = _size;_str = new char[_capacity + 1];//strcpy(_str, str);//strcpy遇到空白符会终止拷贝,memcpy拷贝整个字符串包括'\0'memcpy(_str, str, _size + 1);}//拷贝构造,接收一个对象的引用,复制现有来创建string(const string&s){_size = s._size;_capacity = _size;_str = new char[_size + 1];//strcpy(_str, s._str);memcpy(_str, s._str, _size + 1);}//拷贝构造的现代写法//string(const string& s)//若参数为hello\0world,会发生截断手动初始化,编译器对内置类型不会调用构造函数// :_str(nullptr)// , _size(0)// , _capacity(0)//{// string tmp(_str);//遇到\0停止构造,_str为char风格字符串// swap(tmp);//}void swap(string& s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}//赋值运算符重载,传统写法//string& operator=(const string& s)//{// if (this != &s)// {// char*tmp= new char[s._capacity + 1];// //加1是为了把'\0'带上// memcpy(tmp, s._str, s._size+1);// delete[]_str;// _str = tmp;// _size = s._size;// _capacity = s._capacity;// }// return *this;//}//过渡写法,在函数体内调用拷贝构造进行交换//string& operator=(const string& s)//{// if (this != &s)// {// string tmp(s);// //this->swap(tmp);// swap(tmp);// }// return *this;//}//现代写法,运用std库中swap函数进行交换string& operator=(string s)//传值传参{//传参过程中s已经进行一次深拷贝swap( s);return*this;}//析构函数~string(){_size = _capacity = 0;delete[] _str;_str = nullptr;}//将自定义字符串类对象转化为C风格字符串const char* c_str()const{return _str;}//有效数据个数size_t size()const{return _size;}//可读写char& operator[](size_t pos){assert(pos < _size);return _str[pos];}//只读const char& operator[](size_t pos)const{assert(pos < _size);return _str[pos];}//预存空间void reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];//strcpy(tmp, _str);memcpy(tmp, _str, _size + 1);delete[] _str;_str = tmp;_capacity = n;}}//调整容量void resize(size_t n,char ch='\0'){//分三种情况,n小于容量,等于和大于,统一开辟一块新空间扩容if (n < _size){_size = n;_str[n] = '\0';}else{reserve(n+_size);//拷贝原数据for (size_t i = _size; i < _size+n; i++){_str[i] = ch;}_size += n;//最后一个位置手动赋值_str[_size] = '\0';}}void push_back(char ch){//先检查容量if (_size == _capacity){//采取二倍扩容reserve(_capacity == 0 ? 4 : _capacity * 2);}_str[_size] = ch;_size++;_str[_size] = '\0';}//追加函数void append(const char*str){int len = strlen(str);if (_size + len > _capacity){//_size+len可能比二倍容量还大,至少扩这么多reserve(_size + len);}//拷贝时从_size下一个位置开始,避免前面数据被覆盖//strcpy(_str+_size, str);memcpy(_str + _size, str, len + 1);_size+= len;}string& operator+=(char ch){push_back(ch);return *this;}string& operator+=(const char* str){append(str);return *this;}void insert(size_t pos, size_t n, char ch){assert(pos <= _size);if (_size + n > _capacity){reserve(_size + n);}//挪动数据size_t end = _size;while (end >= pos && end != npos){_str[end + n] = _str[end];end--;}for (size_t i = 0; i < n; i++){_str[pos] = ch;pos++;}_size += n;}void insert(size_t pos, const char* str){assert(pos <= _size);size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}//挪动数据size_t end = _size;while (end >= pos && end != npos){_str[end + len] = _str[end];end--;}//for (size_t i = 0; i < len; i++)//{//_str[pos + i] = str[i];//}//strcpy(_str + pos, str);memcpy(_str + pos, str, len + 1);_size += len;}void erase(size_t pos, size_t len = npos){//len给一个缺省值,代表默认完全删除assert(pos <= _size);//完全删除情况if (npos == len || len + pos >= _size){_str[pos] = '\0';_size = pos;}//部分删除情况(取中间一段删除)else{size_t end = len + pos;while (end <= _size){//挪动的数据_str[pos++] = _str[end++];}_size -= len;}}//从pos位置开始查找size_t 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 find(const char* str, size_t pos=0){assert(pos < _size);//运用字符串中查找子字符串的函数const char* ptr = strstr(_str + pos, str);if (ptr){//返回子字符串在原始字符串中的位置//若直接返回ptr是指向子字符串的一个指针,而不是索引return ptr - _str;}else{return npos;}}string substr(size_t pos = 0, size_t len = npos){assert(pos < _size);size_t n = len;//要查找的子字符串就是原字符串的情况if (pos + len > _size|| len == npos){//第一个条件判断输入len是否超出长度,第二个条件判断是否输入len//为避免栈溢出应将len==npos放到第一个判断条件,若没有传入len的话就不用判断后条件n = _size - pos;}//返回一个新字符串string tmp;tmp.reserve(n);for (size_t i = pos; i < pos+n; i++){tmp += _str[i];}return tmp;}void clear(){_str[0] = '\0';_size = 0;}//手撕实现//bool operator<(const string& s)//{// size_t i1 = 0;// size_t i2 = 0;// while (i1 < _size && i2 < _size)// {// if (_str[i1] <s. _str[i2])// {// return true;// }// else// {// return false;// }// }// // "hello" "hello" false// // "helloxx" "hello" false// // "hello" "helloxx" true// //存在三种情况需要判断// if (i1 == _size && i2 != _size)// {// return true;// }// else// {// return false;// }//}//运用库函数实现bool operator<(const string& s)const{//memcpy不检查空字符,strcmp遇到'\0'就停止比较//指定比较小字符串的长度,指定大的会越界int ret = memcmp(_str, s._str, _size < s._size ? _size : s._size);return ret == 0 ? _size < s._size : ret < 0;//当ret==0时,比较部分相等,长的大//当memcpy返回负值ret<0,返回true,memcpy返回正值,// 与条件判断语句第三部分ret<0相反,返回false}bool operator ==(const string&s)const{return _size == s._size&& memcmp(_str, s._str, _size) == 0;}//已知两个关系运算符,剩余直接复用即可bool operator>(const string&s)const{return !(*this< s)&&!( *this== s);}bool operator>=(const string& s)const{return !(*this < s);}bool operator<=(const string& s)const{return !(*this > s);}bool operator!=(const string& s) const{return !(*this == s);}private:size_t _size;size_t _capacity;char* _str;public:const static size_t npos;};const size_t string::npos = -1;//istream和ostream都是std库中的成员,使用需要加上命名空间(若没展开)ostream& operator << (ostream& out, const string& s){//ostream在库中定义是防拷贝的,得用引用/*for (size_t i = 0; i < s.size(); i++){//这里单个字符的打印就不需要友元去访问类的私有成员out << s[i];}*/for ( auto ch :s)//auto出来没有const属性,需手动添加{out << ch;}return out;}istream& operator >> (istream& in, string& s){s.clear();//>>流提取运算符和scanf一样,会跳过前导空白符,读取过程中// 遇到空格和换行符会终止char ch = in.get();//get每次读取一个字符while (ch == ' ' || ch == '\n')//循环条件为了跳过前导空白字符{ch = in.get();}//创建临时数组来接收字符,避免频繁开辟空间char buff[128];int i = 0;while (ch != ' ' && ch != '\n'){//改变条件可以跳过作为结束标志的字符buff[i++] = ch;if (i == 127){buff[i] = '\0';s += buff;i = 0;}ch = in.get();}//索引i没到127不需要扩容直接跳出循环//没有将buff中临时变量提取到s中去if (i != 0){buff[i] = '\0';s += buff;}return in;}};
- 测试test.cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<string>
#include"string.h"
using namespace std;//测试:构造+迭代器
void teststring1()
{ee::string s1("keep going");cout << s1.c_str() << endl;ee::string s2;//为空即\0,遇到\0直接不打印cout << s2.c_str() << endl;//遍历方式for (size_t i = 0; i < s1.size(); i++){cout << s1[i] << " ";}cout << endl;//修改值/*for (size_t i = 0; i < s1.size(); i++){s1[i]++;}cout<< endl;*///还没重载<<运算符ee::string s3("keep going");auto it = s3.begin();while (it != s3.end()){(*it)++;cout << *it << " ";it++;}cout << endl;//范围forfor(auto ch:s3){cout << ch << " ";}cout << endl;
}
//测试:push_back/append
void teststring2()
{ee::string s1("keep going");cout << s1.c_str() << endl;s1.push_back('!');s1.append(" move on");cout << s1.c_str() << endl;}//测试:insert/erase
void teststring3()
{ee::string s1("keepgoing");s1.insert(9, "****");//字符串末尾默认为'\0',*后面的字符无法打印cout << s1.c_str() << endl;s1.insert(4, 6, '!');cout << s1.c_str() << endl;//s1.erase(s1.begin(), s1.end());s1.erase(4,6);cout << s1.c_str() << endl;s1.erase(1, 30);cout << s1.c_str() << endl;}//测试:substr
void teststring4()
{ee::string url = "ftp://www.baidu.com/?tn=65081411_1_oem_dg";size_t pos1 = url.find("://");if (pos1 != ee::string::npos){ee::string protocol = url.substr(0, pos1);cout << protocol.c_str() << endl;}size_t pos2 = url.find('/', pos1 + 3);if (pos2 != ee::string::npos){ee::string domain = url.substr(pos1 + 3, pos2 - (pos1 + 3));ee::string uri = url.substr(pos2 + 1);cout << domain.c_str() << endl;cout << uri.c_str() << endl;}
}//测试:resize
void teststring5()
{ee::string s("hellow world");s.resize(8);cout << s.c_str() << endl;s.resize(15, 'i');cout << s.c_str() << endl;s.resize(11);cout << s.c_str() << endl;
}//测试:+=运算符
void teststring6()
{// c的字符数组,以\0为终止算长度// string不看\0,以size为终止算长度ee::string s("hello world");s += '\0';s += "6666666";cout << s.c_str() << endl;cout << s << endl;
}//测试:流插入,流提取
void teststring7()
{ee::string s;cin >> s;cout << s << endl;
}//测试:关系运算符
void teststring8()
{ee::string s1("hello");ee::string s2("hello");cout << (s1 < s2) << endl;cout << (s1 > s2) << endl;cout << (s1 == s2) << endl<<endl;ee::string s3("hello");ee::string s4("helloxxx");cout << (s3 < s4) << endl;cout << (s3 > s4) << endl;cout << (s3 == s4) << endl<<endl;ee::string s5("helloxxx");ee::string s6("hello");cout << (s5 < s6) << endl;cout << (s5 > s6) << endl;cout << (s5 == s6) << endl << endl;
}//测试:赋值运算符重载
void teststring9()
{ee::string s1("hello");ee::string s2(s1);cout << s1 << endl;cout << s2 << endl;ee::string s3("xxxxxxxxxxxxx");s1 = s3;cout << s1 << endl;cout << s3 << endl;
}
int main()
{teststring9();return 0;
}
相关文章:
详解string类+迭代器
迭代器 概念:在 C 中,迭代器是访问容器(如数组、列表、向量、字符串等)元素的一种方式。迭代器提供了一种统一的接口,使得你可以使用相同的代码来遍历不同类型的容器。迭代器本质上是一个指针或者指针的封装࿰…...
OpenCV DNN 模块使用指南
OpenCV DNN 模块使用指南 一、模块概述 OpenCV 的 DNN(深度神经网络)模块为开发者提供了强大的深度学习功能,能够加载并运行多种格式的预训练深度学习模型。此模块广泛应用于图像分类、目标检测、语义分割等众多计算机视觉任务。接下来&…...
温度(Temperature)在大模型输出中的作用与底层原理
温度(Temperature)在大模型输出中的作用与底层原理 在深度学习领域,尤其是自然语言处理(NLP)中,大型语言模型(LLM)的输出生成通常依赖于概率分布的采样。温度(temperatu…...
智能汽车图像及视频处理方案,支持视频智能拍摄能力
美摄科技,作为智能汽车图像及视频处理领域的先行者,凭借其卓越的技术实力和前瞻性的设计理念,为全球智能汽车制造商带来了一场视觉盛宴的革新。我们自豪地推出——美摄科技智能汽车图像及视频处理方案,一个集高效性、智能化、画质…...
一文讲清 C++ CRTP(Curiously Recurring Template Pattern,奇异递归模板模式)
CRTP是 C 中的一种模板元编程技术,其核心原理是通过模板继承和静态多态,在编译期实现基类对派生类成员的访问,从而避免运行时虚函数调用的开销。 1. CRTP 的基本结构 CRTP 的核心思想是:基类是一个模板类,其模板参数…...
如何用Function Calling解锁OpenAI的「真实世界」交互能力?(附Node.js 实战)
一、Function Calling:大模型的「手脚延伸器」 1.1 核心定义 Function Calling是OpenAI在2023年6月13日推出的革命性功能(对应模型版本gpt-3.5-turbo-0613和gpt-4-0613),允许开发者通过自然语言指令触发预定义函数,实…...
MySQL:float,decimal(1)
会四舍五入 无符号浮点数 更改321zyy1下的salary 为float类型 decimal:...
基于DrissionPage的DY无水印视频采集
基于DrissionPage的Dy无水印视频采集技术解析 目录 一、项目背景与痛点分析 1.1 典型应用场景1.2 传统方案痛点对比1.3 需求分析流程图二、系统架构设计 2.1 核心架构图2.2 执行流程图解三、开发环境配置 3.1 版本要求3.2 环境搭建指南四、实现流程详解 4.1 主要处理阶段4.2 关…...
yt-dlp工具下载视频使用方法
使用 yt-dlp 下载有土播视频时,可以很容易地同时下载字幕文件。 下面是详细操作说明: 一、下载视频并同时下载字幕: 基础命令: yt-dlp --write-subs 视频链接示例: yt-dlp --write-subs https://www.youdubo.com/wa…...
canvas数据标注功能简单实现:矩形、圆形
背景说明 基于UI同学的设计,在市面上找不到刚刚好的数据标注工具,遂决定自行开发。目前需求是实现图片的矩形、圆形标注,并获取标注的坐标信息,使用canvas可以比较方便的实现该功能。 主要功能 选中图形,进行拖动 使…...
Linux与HTTP报头属性和请求方式
HTTP报头属性、请求方式 本篇介绍 在上一节深入HTTP序列化和反序列化已经详细讲解了HTTP是如何进行序列化和反序列化的,但是上一节对请求报头和响应报头的具体内容并没有做出具体的说明,本节就会基于这个问题继续探讨HttpServer;另外在介绍…...
WordPress漏洞
一,后台修改模板拿WebShell 1,安装好靶场后访问 2,在如图所示的位置选择一个php文件写入一句话木马,我们这里选择在404.php中写入 3,访问404.php 二,上传主题拿WebShell 1,找到如图所示的页面…...
go命令使用
查看配置信息 go env配置go国内源 export GO111MODULEon export GOPROXYhttps://goproxy.cn测试 go install github.com/jesseduffield/lazydockerlatesthttps://github.com/jesseduffield/lazydocker...
uniapp vue3使用uniapp的生命周期
使用uniapp的onLoad等生命周期 // 需要引入 import { onLoad , onShow } from dcloudio/uni-app; // 箭头函数 onLoad(()>{//内容 })使用vue生命周期 vue官方文档:https://cn.vuejs.org/api/options-lifecycle.html import { ref,onMounted } from vue; onMou…...
nginx vue history模式 try_files
server {listen 80;server_name localhost chat.test.com;#配置根目录location / {root /temp/test;#index index.html index.htm;try_files $uri $uri/ /index.html;add_header Content-Security-Policy upgrade-insecure-requests;}} https://blog.csdn.net/xutongbao/…...
【css酷炫效果】纯CSS实现悬浮弹性按钮
【css酷炫效果】纯CSS实现悬浮弹性按钮 缘创作背景html结构css样式完整代码效果图 想直接拿走的老板,链接放在这里:https://download.csdn.net/download/u011561335/90492020 缘 创作随缘,不定时更新。 创作背景 刚看到csdn出活动了&…...
MySQL自动化配置工具开发:探索如何用脚本实现MySQL一键安装与配置,提升运维效率
引言 MySQL作为最流行的开源关系型数据库之一,广泛应用于各类业务场景。然而,手动安装和配置MySQL不仅耗时,还容易出错。为了提高运维效率,开发一款MySQL自动化配置工具显得尤为重要。本文将探索如何通过脚本实现MySQL的一键安装与配置,并提供具体的配置脚本文档和关键参…...
如何查看安卓版本号的方法(例如查看是13、12、11、10...)
开发过程中需要了解到安卓版本号是多少,那么以下有三种方法可以知晓安卓手机的Android版本号。 方法1:手机设置直接查看 1.打开【设置】 --> 滑动到手机最底部 --> 点击【关于手机】或 【系统】--> 选择【Android版本】 2.直接查看版本号&am…...
ubuntu 解挂载时提示 “umount: /home/xx/Applications/yy: target is busy.”
问题如题所示,我挂载一个squanfs文件系统到指定目录,当我使用完后,准备解挂载时,提示umount: /home/xx/Applications/yy: target is busy.,具体的如图所示, 这种提示通常是表明这个路径的内容正在被某些进…...
Java XML与JSON相互转换详解
目录 一、为什么需要XML与JSON转换二、使用Jackson库进行转换1. 添加依赖2. XML转JSON3. JSON转XML三、注意事项在现代软件开发中,数据格式的转换是一项常见的任务,特别是在处理不同系统或服务之间的数据交换时。XML(可扩展标记语言)和JSON(JavaScript对象表示法)是两种广…...
[AI速读]用持续集成(CI)优化芯片验证环境:Jenkins与EDA工具的实战指南
在芯片验证中,回归测试(Regression Test)是确保设计稳定性的关键步骤。但随着设计复杂度增加,手动管理海量测试用例、分析日志和覆盖率数据变得异常耗时。本文将介绍如何利用持续集成(CI)工具Jenkins,结合EDA验证环境(如Cadence vManager),实现自动化测试与结果分析,…...
Java-SpringBootWeb入门、Spring官方脚手架连接不上解决方法
一. Spring 官网:Spring | Home Spring发展到今天已经形成了一种开发生态圈,Spring提供了若干个子项目,每个项目用于完成特定的功能(Spring全家桶) Spring Boot可以帮助我们非常快速的构建应用程序、简化开发、提高效率 。 二. Spring Boot入…...
WEB攻防-PHP反序列化-字符串逃逸
目录 前置知识 字符串逃逸-减少 字符串逃逸-增多 前置知识 1.PHP 在反序列化时,语法是以 ; 作为字段的分隔,以 } 作为结尾,在结束符}之后的任何内容不会影响反序列化的后的结果 class people{ public $namelili; public $age20; } var_du…...
Android第五次面试总结(网络篇)
一、引言:为什么需要 DNS? 在浏览器输入www.example.com的瞬间,一场跨越全球的数字接力赛悄然启动。DNS(域名系统)如同互联网的 “电话号码簿”,将人类可读的域名转化为机器可识别的 IP 地址。本文将以工程…...
全网首创/纯Qt/C++实现国标GB28181服务/实时视频/云台控制/预置位/录像回放和下载/事件订阅/语音对讲
一、前言说明 用纯Qt来实现这个GB28181的想法很久了,具体可以追溯到2014年,一晃十年都过去了,总算是整体的框架和逻辑都打通了,总归还是杂七杂八的事情多,无法静下心来研究具体的协议,最开始初步了解协议后…...
详解MySQL的事务实现机制
MySQL事务实现机制 1. 锁机制2. Redo Log(重做日志)3. Undo Log(撤销日志)4. MVCC(多版本并发控制)综合事务处理流程 在MySQL中(主要以InnoDB为例),事务的实现依赖于多个…...
什么是 BA ?BA怎么样?BA和BI是什么关系?
前几天有朋友在评论区提到了BA这个角色,具体是干什么的,我大概来说一下。 什么是BA BA 英文的全称是Business Analyst,从字面上意思就是商业分析师,做过商业智能BI项目的应该比较了解。实际上以我个人的经验,BA 的角…...
Junit在测试过程中的使用方式,具体使用在项目测试中的重点说明
JUnit 是一个广泛使用的 Java 单元测试框架,主要用于编写和运行可重复的测试。以下是 JUnit 在项目测试中的使用方式和重点说明: 1. 基本使用 场景:测试一个简单的 Java 类。 示例: import org.junit.Test; import static org.junit.Assert.*;public class CalculatorTe…...
网络安全之前端学习(HTML篇)
前言:网络安全中有一个漏洞叫xss漏洞,就是利用网页引发弹窗,这就要求我们看得懂源码,所以我会持续更新前端学习,可以不精通,但是一定要会,主要掌握HTML,css,js这三项技术…...
Lineageos 22.1(Android 15)实现负一屏
一、前言 方案是参考的这位大佬的,大家可以去付费订阅支持一波。我大概理一下Android15的修改。 大佬的方案代码 二、Android15适配调整 1.bp调整,加入aidl引入,这样make之后就可以索引代码了 filegroup {name: "launcher-src"…...
Redisson分布式锁(超时释放及锁续期)
🍓 简介:java系列技术分享(👉持续更新中…🔥) 🍓 初衷:一起学习、一起进步、坚持不懈 🍓 如果文章内容有误与您的想法不一致,欢迎大家在评论区指正🙏 🍓 希望这篇文章对你有所帮助,欢…...
SQL授予用户查询某个模式或者具体某个表
例如:需要给sjzt授权查询这个模式下的这个表的操作 数据库:DB_HZ_HYZHFZ 模式:hn_proxy 服务注册表:hn_proxy."serverrg" 账号:sjzt -- 登录到目标数据库 DB_HZ_HYZHFZ -- 授予权限给账号 sjzt 在模式 hn_proxy 中访问…...
Burp Suite 代理配置与网络通信
目录 1. 引言 2. Burp 代理基础配置 2.1 浏览器代理设置 2.2 Burp 监听端口配置 2.3 常见错误排查 3. 网络问题解决 3.1 端口占用检查 3.2 防火墙配置 3.3 证书信任问题 4. 虚拟机环境配置 4.1 NAT 模式与端口转发 4.2 桥接模式配置 4.3 跨设备访问测试 5. 技术概…...
PyCharm安装redis,python安装redis,PyCharm使用失败问题
报错信息 Usage: D:\wb2\wbrj_pys\venv\Scripts\python.exe -m pip install [options] [package-index-options] … D:\wb2\wbrj_pys\venv\Scripts\python.exe -m pip install [options] -r [package-index-options] … D:\wb2\wbrj_pys\venv\Scripts\python.exe -m pip instal…...
使用命令行工具控制wireshark对抓包文件进行针对性处理的总结
近日,工作中有开发对抓包文件进行针对性过滤的小程序的需求,兜兜转转踩了很多坑后还是绕回了wireshark。 作为最出名的开源软件之一,wireshark也具有使用命令行进行操作的功能,这就是我们今天会总结到的“tshark.exe”。 通常&a…...
【STM32实物】基于STM32的太阳能充电宝设计
基于STM32的太阳能充电宝设计 演示视频: 基于STM32的太阳能充电宝设计 硬件组成: 系统硬件包括主控 STM32F103C8T6、0.96 OLED 显示屏、蜂鸣器、电源自锁开关、温度传感器 DS18B20、继电器、5 V DC 升压模块 、TB4056、18650锂电池、9 V太阳能板、稳压降压 5 V三极管。 功能…...
NLP 与常见的nlp应用
自然语言处理(NLP)是一个广泛的领域,它不仅包括自然语言理解(NLU),还涉及一系列其他任务和子领域。以下是NLP领域中的主要组成部分及其相关任务: 1. 自然语言理解(NLU) …...
基于javaweb的SSM+Maven宠物领养宠物商城流浪动物管理系统与实现(源码+文档+部署讲解)
技术范围:SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容:免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论…...
Java数据类型 Arrays VS ArraysList VS LikedList 解析
在学习Java过程中,在刷题时总是搞不清楚这三种数据结构的区别,打算写篇文章记录一下 Arrays VS ArrayList ArrayList VS LinkedList 总结 Arrays VS ArrayList ArraysArrayList类型Java的基本数据类型Java集合框架中的一个类,实现了List接…...
C++进阶(一)
个人主页:PingdiGuo_guo 收录专栏:C干货专栏 前言 本篇博客是讲解函数的重载以及引用的知识点的。 文章目录 前言 1.函数重载 1.1何为函数重载 1.2函数重载的作用 1.3函数重载的实现 2.引用 2.1何为引用 2.2定义引用 2.3引用特性 2.4常引用 2…...
Elasticsearch 在航空行业:数据管理的游戏规则改变者
作者:来自 Elastic Adam La Roche 数字化客户体验不再是奢侈品,而是欧洲航空公司必不可少的需求。它推动了客户满意度,提升了运营效率,并创造了可持续的竞争优势。随着行业的不断发展,优先投资前沿数字技术和平台的航空…...
Powershell WSL部署ubuntu22.04.5子系统
前提条件WSL 安装 wsl 安装参考1wsl 安装csdn参考2wsl 百度网盘离线下载 本地目录安装ubuntu22.04.5 子系统 powershell 管理员打开执行(实现,下载安装ubuntu子系统,用户创建,远程ssh登录设置,防火墙端口开放)子系统IP 查看方法wsl...
华为网路设备学习-16 虚拟路由器冗余协议(VRRP)
VRRP是针对干线上三层网络设备(如:路由器、防火墙等)的网络虚拟化技术,提供冗余和状态监测等功能。确保在网络中的单点故障发生时,能够快速切换到备份设备,从而保证网络通信的连续性和可靠性。 VRRP通过…...
SQLark中如何进行数据筛选与排序
本文将为你介绍在 SQLark 中如何进行数据筛选与排序,掌握这些操作能够极大提升你的工作效率。 SQLark官网链接:www.sqlark.com 数据筛选 在数据库操作中,数据筛选是一项关键功能,它依据特定条件对数据进行过滤,帮助用户从海量数据…...
slq-labs日志
上次写了第五关的布尔盲注,和双查询报错注入,还有其他报错注入的方法 一.extractvalue函数 extractvalue函数 原理 extractvalue 是一个在 XML 数据中提取值的 MySQL 内置函数。它的语法为: EXTRACTVALUE(xml, xpath_expr)xml 是 XML 字符…...
Webrtc编译官方示例实现视频通话
Webrtc编译官方示例实现视频通话 前言 webrtc官网demo中给了一个供我们学习和应用webrtc的一个很好的例子:peerconnection,这期我们就来编译和运行下这个程序看看视频通话的效果以。 1、打开源码工程 继上期源码编译完成后,我们使用vs打开…...
对接SaToken @SaCheckEL 鉴权注解
对接SaToken SaCheckEL 鉴权注解 文章目录 对接SaToken SaCheckEL 鉴权注解前言一、引入插件和配置SaToken属性配置二、配置1.自定义SaTokenInterceptor并注入Bean2.WebMvcConfig配置SaTokenInterceptor,拦截所有请求路径 三、重载SaToken权限接口和方法注入SaCheck…...
【数据结构】顺序表(附源码)
数据结构之顺序表 1、线性表2、顺序表2.1 概念与结构2.2 顺序表的分类 3、动态顺序表3.1 结构3.2 初始化3.3 容量检查3.4 尾部插入数据3.5 删除尾部数据3.6 头部插入数据3.7 头部删除数据3.8 任意位置pos插入数据3.9 任意位置pos删除数据3.10 查找 4、动态顺序表实现的源码 1、…...
java使用Apache POI 操作word文档
项目背景: 当我们对一些word文档(该文档包含很多的标题比如 1.1 ,1.2 , 1.2.1.1, 1.2.2.3)当我们删除其中一项或者几项时,需要手动的对后续的进行补充。该功能主要是对标题进行自动的补充。 具…...
【Linux网络编程】网络基础
目录 1、OSI分层思想2、数据的封装3、ip地址ipv4的ip地址分类:为什么这样划分?举例 4、端口号5、tcp三次握手为什么需要三次握手?tcp连接的标识为什么 IP 地址不能单独标识一条连接? 6、tcp四次挥手为什么需要四次挥手为什么需要三…...