关于标准库中的string类 - c++
目录
关于string类
string类的常用接口
string类常用接口的简单模拟实现
关于string类
string类在cplusplus.com的文档介绍
- 1. string是表示字符串的字符串类
- 2. 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。
- 3. string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator>string;
- 4. 不能操作多字节或者变长字符的序列。
string类的常用接口
void Teststring()
{string s1; // 构造空的string类对象s1string s2("hello world"); // 用C格式字符串构造string类对象s2string s3(s2); // 拷贝构造s3
}
#include <iostream>
using namespace std;#include <string>//-----------------------------------------------------------------------
//测试string容量相关接口
//size / capacity / clear / resizevoid Teststring()
{//注意:string类对象支持直接用cin和cout进行输入和输出string s1("hello world");cout << s1.size() << endl;cout << s1.length() << endl;cout << s1.capacity() << endl;cout << s1 << endl;//将s1中的字符串清空,注意清空时只是将size清0,不改变底层空间的大小s1.clear();cout << s1.size() << endl;cout << s1.capacity() << endl;//将s1中的有效字符个数增加到6个,并且使用'*'来进行填充s1.resize(6, '*');cout << s1.size() << endl;cout << s1.capacity() << endl;cout << s1 << endl;// 将s1中有效字符个数增加到15个,多出位置用缺省值'\0'进行填充// 注意此时s中有效字符个数已经增加到15个s1.resize(15);cout << s1.size() << endl;cout << s1.capacity() << endl;cout << s1 << endl;//另外,resize也有删除有效字符的功能,但是不会改变底层空间的大小s1.resize(1);cout << s1.size() << endl;cout << s1.capacity() << endl;cout << s1 << endl;
}//-----------------------------------------------------------------------
//测试reserve
//1.是否改变string类中间的有效元素个数
//2.当reserve参数小于string底层空间时,是否会将空间缩小void TestReserve()
{string s1;s1.reserve(100);cout << s1.size() << endl;cout << s1.capacity() << endl;s1.reserve(10);cout << s1.size() << endl;cout << s1.capacity() << endl;
}int main()
{Teststring();TestReserve();return 0;
} - 1. size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一致,一般情况下基本都是用size()。
- 2. clear()只是将string中有效字符清空,不改变底层空间大小。
- 3. resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。
- 4. reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于string的底层空间总大小时,reserver不会改变容量大小。
#include <iostream>
using namespace std;#include <string>//---------------------------------------------------------------------
// string的遍历及访问
// 1.迭代器:begin()+end() / rbegin()+ rend() ...
// 2.for+operator[]
// 3.范围for:底层实现是使用迭代器,实际上是迭代器的封装 (范围forC++11后才支持)void Teststring()
{string s1("hello world");//1.string::iterator it = s1.begin();while (it != s1.end()){cout << *it;++it;}cout << endl;//2.for (size_t i = 0; i < s1.size(); ++i){cout << s1[i];}cout << endl;//3.for (auto ch : s1){cout << ch;}cout << endl;
}int main()
{Teststring();return 0;
}
#include <iostream>
using namespace std;#include <string>//---------------------------------------------------------------------
//string的测试与修改
//1.插入(拼接)方式:push_back / append / operator+=
//2.任意位置插入:insert
//3.查找 / 反向查找:find / rfind
//4.替换字符:replace
//5.截取子串 :substr
//6.读取字符:geline
//7.删除:erase
//8.返回c形式字符串 : c_strvoid TestString()
{string s1("hello world");//插入一个字符s1.push_back(' ');s1.push_back('i'); cout << s1 << endl;//插入一串字符s1.append(" ");s1.append("love");cout << s1 << endl;//c++更喜欢使用+=s1 += ' ';s1 += "you";cout << s1 << endl;
}void TestInsert()
{string s1("hello");//任意位置插入s1.insert(5, 1, ' ');s1.insert(6, "world");cout << s1 << endl;//使用迭代器s1.insert(s1.begin(), '!');s1.insert(s1.begin()+1, ' ');cout << s1 << endl;
}void TestFind_Replace()
{//笔试题:将下列所有空格都改成 %20//思路1.string str("hello world i love you");size_t pos = str.find(' ');while (pos != string::npos){str.replace(pos, 1, "%20");pos = str.find(' ');}cout << str << endl;//====================================//优化://string str("hello world i love you");1.提前开空间,减少扩容消耗的效率//size_t num = 0;//for (auto ch : str)//{// if (ch == ' ')// {// num++;// }//}//str.reserve(str.size() + 2 * num);//size_t pos = str.find(' ');//while (pos != string::npos)//{// str.replace(pos, 1, "%20");// //2.避免重复访问数据,提高效率// pos = str.find(' ', pos+3);//}//cout << str << endl;//==================================//思路2.//string str("hello world i love you");//string newStr;//size_t num = 0;//for (auto ch : str)//{// if (ch == ' ')// {// num++;// }//}//newStr.reserve(newStr.size() + 2 * num);//for (auto ch : str)//{// if (ch != ' ')// {// newStr += ch;// }// else// {// newStr += "%20";// }//}//cout << newStr << endl;}void TestFind_Substr()
{//笔试题:获取ulr中的域名string url("http://www.cplusplus.com/reference/string/");cout << url << endl;size_t start = url.find("://");if (start == string::npos){cout << "invalid url" << endl;return;}start += 3;size_t finish = url.find('/', start);string address = url.substr(start, finish - start);cout << address << endl;}void TestRfind_Getline()
{//笔试题:求字符串最后出现单词的长度string str;//注意:为什么这里使用 getline 而不使用 cin //因为 cin 类似于scanf ,读取字符遇到空格或者\0就会终止,无法读取后面的字符//使用c++提供了 getline :遇到换行符才会终止读取getline(cin, str);size_t pos = str.rfind(' ');if (pos != string::npos){cout << str.size() - pos - 1 << endl;}else{//只有一个单词的情况cout << str.size() << endl;}}void TestErase()
{string str("hello world");str.erase(5, 1);cout << str << endl;str.erase(5, 10);cout << str << endl;str.erase(1);cout << str << endl;
}void TestC_str()
{string str("hello world");cout << str << endl;cout << str.c_str() << endl;cout << str << endl;cout << (void*)str.c_str() << endl;str += ' ';str += '\0';str += "******";cout << str << endl;cout << str.c_str() << endl;
}int main()
{TestString();TestInsert();TestFind_Replace();TestFind_Substr();TestRfind_Getline();TestErase();TestC_str();return 0;
}
- 1. 在string尾部追加字符时,s.push_back(c) / s.append(1, c) / s += 'c'三种的实现方式差不多,一般情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可以连接字符串。
- 2. 对string操作时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留好。
string类常用接口的简单模拟实现
#include <iostream>using namespace std;#include <assert.h>
#include<string>class _string
{public://模拟实现常用接口//...private:char* _str;size_t size;size_t capacity;static const size_t npos;
};const size_t string::npos = -1; 注:npos
关于npos ,这里建议类里面声明,类外面定义,不建议在类里面给缺省值。因为成员变量给缺省值是因为会在初始化列表进行初始化,但是strtic修饰的静态成员变量,不能给缺省值,因为静态成员变量存储位置在静态区,属于整个工程。
另外,c++11有一个值得吐槽的地方,就是开了一个特例,如果加const ,那么整型静态成员变量可以给缺省值,如下图:

_string():_str(new char[1]), _size(0),_capacity(0){_str[0] = '\0';}_string(const char* str):_size(strlen(str)){_capacity = _size;_str = new char[_capacity + 1];strcpy(_str, str);} 问:为什么无参的字符串构造函数 _str(new char[1]) 初始化不给 nullptr 要给一个空间且加上[ ]?
答:因为如果给nullptr的话,cout是会对_str进行解引用,这样会导致程序崩溃,所以才会给一个空间。而给[ ]是为了在析构的时候与delete[ ] 保持一致。
问:为什么 _str = new char[_capacity + 1] 中要+1?
答:因为_capacity是容量字符,指的是能够存取多少个有效字符,而vs认为 '\0'属于标识符,不属于有效字符的范畴,所以+1是为了给'\0'预留空间。
- 优化:以上两个函数可以优化为缺省函数
_string(const char* str = "\0"):_size(strlen(str)){_capacity = _size;_str = new char[_capacity + 1];strcpy(_str, str);}
_string(const _string& str):_size(str._size),_capacity(str._capacity){_str = new char[_capacity + 1];strcpy(_str, str._str);} 2.赋值 =
_string operator=(const _string& str){delete[] _str;_str = new char[str._capacity + 1];strcpy(_str, str._str);_size = str._size;_size = str._capacity;return *this;} - 上述代码有一个问题,就是程序开始就将_str的空间进行释放,这样可能会导致数据的丢失,所以需要进行优化。
_string operator=(const _string& str){if (this != &str){char* tmp = new char[str._capacity + 1];strcpy(tmp, str._str);delete[] _str;_str = tmp;_size = str._size;_capacity = str._capacity;}return *this;} 3.析构函数 ~
~_string(){delete[] _str;_str = nullptr;_size = _capacity = 0;} 4.成员访问
char& operator[](size_t pos){assert(pos < _size);return _str[pos];}const char& operator[](size_t pos) const{assert(pos < _size);return _str[pos];}size_t size() const{return _size;}size_t capacity() const{return _capacity;}const char* c_str(){return _str;}
typedef char* iterator;iterator begin(){return _str;}iterator end(){return _str + _size;} 6.关系运算符(relational operators)string
bool operator>(const _string& str) const{return strcmp(_str, str._str) > 0;}bool operator==(const _string& str) const{return strcmp(_str, str._str) == 0;}bool operator>=(const _string& str) const{return *this > str || *this == str;}bool operator<(const _string& str) const{return !(*this >= str);}bool operator<=(const _string& str) const{return !(*this > str);}bool operator!=(const _string& str) const{return !(*this == str);} 7.reserve
void reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}} 问:这里为什么要加个 if 进行判断?
答:因为如果不进行判断,当 n < _capacity 的时候,会导致缩容的问题,对程序的安全造成隐患,因此要加个判断,避免出现缩容的情况。
void push_back(char ch){if (_size + 1 > _capacity){reserve(_capacity * 2);}_str[_size] = ch;_size++;_str[_size] = '\0';} 上述代码有一个隐藏的问题,那就是如果 _capacity 如果为 0 ,这个时候进行push_back,代码就会越界,从而崩溃。解决办法有两个,一是修改一下构造函数,而是进行判断,如果_capacity 为 0 就直接进行赋值。
_string(const char* str = "\0"):_size(strlen(str)){_capacity = _size == 0 ? 3 : _size;_str = new char[_capacity + 1];strcpy(_str, str);}
void append(const char* str){if (_size + 1 > _capacity){reserve(_capacity * 2);}size_t len = strlen(str);strcpy(_str + _size, str);_size += len;} 问:这里为什么不适用strcat?
答:因为 strcat 是自己去寻找 '\0' 的位置,而 _str + len 就是 '\0' 的位置 ,strcpy 函数会把需要拷贝的字符串最后的位置的 '\0'拷贝过来。
_string& operator+=(char ch){push_back(ch);return *this;}_string& operator+=(const char* str){append(str);return *this;}
void resize(size_t n, char ch = '\0')
{if (n <= _size){//保留前n个 - 删除数据_size = n;_str[_size] = '\0';}else //n > size{if (n > _capacity){reserve(n);}size_t i = _size;while (i < n){_str[i++] = ch;}_size = n;_str[_size] = '\0';}
}
void insert(size_t pos, char ch){assert(pos <= _size);if (_size + 1 > _capacity){reserve(_capacity * 2);}size_t end = _size;while (end >= pos){_str[end + 1] = _str[end];--end;}_str[pos] = ch;++_size;} 以上代码有个问题,那就是 end 属于无符号整型,而 pos 也属于无符号整型,这里会造成程序的死循环。即使把 end 置成 int 类型,但是也会发生隐式类型转换,有符号会转换成无符号。也不建议进行强转,所以解决办法的话,就是修改挪动数据的逻辑。

优化:
void insert(size_t pos, char ch){assert(pos <= _size);if (_size + 1 > _capacity){reserve(_capacity * 2);}size_t end = _size + 1;while (end > pos){_str[end] = _str[end - 1];--end;}_str[pos] = ch;++_size;} 当然,insert 还需要重载插入字符串
void insert(size_t pos, const char* str){assert(pos <= _size);size_t len = strlen(str);if (_size + len > _capacity){reserve(_capacity * 2 + len);}size_t end = _size + len;while (end > pos + len - 1){_str[end] = _str[end - len];--end;}strncpy(_str + pos, str, len);_size += len;} 这里之所以选择 strncpy 而不是 strcpy ,因为 strcpy 会拷贝字符串结尾的标识符 '\0'。
void erase(size_t pos, size_t len = npos){//尾部直接删除if (pos + len > _size || len == npos){_str[pos] = '\0';_size = pos;}else //挪动数据{strcpy(_str + pos, _str + pos + len);_size -= len;}}
void swap(_string& str){std::swap(_str, str._str);std::swap(_size, str._size);std::swap(_capacity, str._capacity);}
size_t find(char ch, size_t pos = 0){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);char* p = strstr(_str + pos, str); //strstr : 返回第一次指针匹配的位置if (p == nullptr){return npos;}else{return p - _str;}}
ostream& operator<<(ostream& out, const string& str)
{for (auto ch : str){out << str;}return out;
} 注:因为在类域中存在this指针,而友元函数又会增加耦合度,破环封装,所以这里建议流插入或者流提取的实现不写在类域之中。
istream& operator>>(istream& in, string& str)
{char ch;in >> ch;while (ch != ' ' && ch != '\n'){str += ch;in >> ch;}return in;
} 问:为什么如果输入多组字符,中间用空格或者换行隔开的话,编译器只能拿到第一组字符,拿不到后面的字符。
答:因为cin或者sancf读取的时候会默认忽然空格或者换行,不进行识别,默认空格或者换行是多个值之间的间隔。流提取并未在输入中获取字符,而是在缓冲区获取字符,而空格或者换行未进入缓冲区,c++/c 规定,值与值之间的区分必须是空格或者换行,所以输入空格或者换行会被认为是多个字符之间的间隔,不会被cin 或者 scanf 拿到。
优化:
istream& operator>>(istream& in, string& str)
{str.clear(); //清除掉之前的字符char ch = in.get(); //get()函数不区分间隔while (ch != ' ' && ch != '\n'){str += ch;ch = in.get();}return in;
} 上述版本只是一个简单的版本,实际实现可能有些复杂
istream& operator>>(istream& in, string& str)
{str.clear(); //清除掉之前的字符char ch = in.get(); //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;}ch = in.get();}if (i != 0) //防止最后的数据没有添加进去{buff[i] = '\0';str += buff;}return in;
}
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>using namespace std;#include <string>
#include <assert.h>class _string
{public:typedef char* iterator;iterator begin(){return _str;}iterator end(){return _str + _size;}//模拟实现 //_string()// :_str(new char[1])// , _size(0)// ,_capacity(0)//{// _str[0] = '\0';//}_string(const char* str = "\0"):_size(strlen(str)){_capacity = _size == 0 ? 3 : _size;_str = new char[_capacity + 1];strcpy(_str, str);}_string(const _string& str):_size(str._size), _capacity(str._capacity){_str = new char[_capacity + 1];strcpy(_str, str._str);}_string operator=(const _string& str){if (this != &str){char* tmp = new char[str._capacity + 1];strcpy(tmp, str._str);delete[] _str;_str = tmp;_size = str._size;_capacity = str._capacity;}return *this;}char& operator[](size_t pos){assert(pos < _size);return _str[pos];}const char& operator[](size_t pos) const{assert(pos < _size);return _str[pos];}size_t size() const{return _size;}size_t capacity() const{return _capacity;}const char* c_str(){return _str;}bool operator>(const _string& str) const{return strcmp(_str, str._str) > 0;}bool operator==(const _string& str) const{return strcmp(_str, str._str) == 0;}bool operator>=(const _string& str) const{return *this > str || *this == str;}bool operator<(const _string& str) const{return !(*this >= str);}bool operator<=(const _string& str) const{return !(*this > str);}bool operator!=(const _string& str) const{return !(*this == str);}void reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}void push_back(char ch){if (_size + 1 > _capacity){reserve(_capacity * 2);}_str[_size] = ch;_size++;_str[_size] = '\0';}void append(const char* str){if (_size + 1 > _capacity){reserve(_capacity * 2);}size_t len = strlen(str);strcpy(_str + _size, str);_size += len;}_string& operator+=(char ch){push_back(ch);return *this;}_string& operator+=(const char* str){append(str);return *this;}void resize(size_t n, char ch = '\0'){if (n <= _size){//保留前n个 - 删除数据_size = n;_str[_size] = '\0';}else //n > size{if (n > _capacity){reserve(n);}size_t i = _size;while (i < n){_str[i++] = ch;}_size = n;_str[_size] = '\0';}}void insert(size_t pos, char ch){assert(pos <= _size);if (_size + 1 > _capacity){reserve(_capacity * 2);}size_t end = _size + 1;while (end > pos){_str[end] = _str[end - 1];--end;}_str[pos] = ch;++_size;}void insert(size_t pos, const char* str){assert(pos <= _size);size_t len = strlen(str);if (_size + len > _capacity){reserve(_capacity * 2 + len);}size_t end = _size + len;while (end > pos + len - 1){_str[end] = _str[end - len];--end;}strncpy(_str + pos, str, len);_size += len;}void erase(size_t pos, size_t len = npos){//尾部直接删除if (pos + len > _size || len == npos){_str[pos] = '\0';_size = pos;}else //挪动数据{strcpy(_str + pos, _str + pos + len);_size -= len;}}void swap(_string& str){std::swap(_str, str._str);std::swap(_size, str._size);std::swap(_capacity, str._capacity);}size_t find(char ch, size_t pos = 0){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);char* p = strstr(_str + pos, str); //strstr : 返回第一次指针匹配的位置if (p == nullptr){return npos;}else{return p - _str;}}void clear(){_str[0] = '\0';_size = 0;}~_string(){delete[] _str;_str = nullptr;_size = _capacity = 0;}private:char* _str;size_t _size;size_t _capacity;//static const size_t npos;static const size_t npos = -1;
};//const size_t string::npos = -1;ostream& operator<<(ostream& out, const string& str)
{for (auto ch : str){out << str;}return out;
}istream& operator>>(istream& in, string& str)
{str.clear(); //清除掉之前的字符char ch = in.get(); //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;}ch = in.get();}if (i != 0) //防止最后的数据没有添加进去{buff[i] = '\0';str += buff;}return in;
}
以上仅代表个人看法,欢迎讨论
相关文章:
关于标准库中的string类 - c++
目录 关于string类 string类的常用接口 string类常用接口的简单模拟实现 关于string类 string类在cplusplus.com的文档介绍 1. string是表示字符串的字符串类 2. 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。 3. string在…...
Chrome添加扩展程序
Crx4Chrome 下载crx 打开扩展程序 如果拖动crx文件到扩展程序提示只能通过Chrome应用商店添加此项内容 修改crx文件后缀为zip并解压,再拖动到扩展程序 Vue.js devtools...
C++单调向量算法:132模式枚举1简洁版
本题不同解法 包括题目及代码C二分查找算法:132 模式解法一枚举3C二分查找算法:132 模式解法二枚举2代码简洁C二分查找算法:132 模式解法三枚举1性能最佳C单调向量算法:132 模式解法三枚举1代码更简洁C二分查找算法:1…...
【ARFoundation学习笔记】2D图像检测跟踪
写在前面的话 本系列笔记旨在记录作者在学习Unity中的AR开发过程中需要记录的问题和知识点。主要目的是为了加深记忆。其中难免出现纰漏,更多详细内容请阅读原文以及官方文档。 汪老师博客 文章目录 2D图像检测创建一个图像检测工程图像追踪的禁用和启用多图像追踪…...
计算机算法分析与设计(24)---分支限界章节复习
文章目录 一、分支界限法介绍二、旅行商问题应用三、装载问题应用3.1 问题介绍与分析3.2 例题 四、0-1背包问题应用4.1 问题介绍与分析4.2 例题 一、分支界限法介绍 二、旅行商问题应用 三、装载问题应用 3.1 问题介绍与分析 3.2 例题 四、0-1背包问题应用 4.1 问题介绍与分析…...
二十三种设计模式-解密状态模式:优雅地管理对象状态
在软件开发中,经常会遇到需要根据对象的状态来改变其行为的情况。传统的实现方式可能涉及到大量的条件语句或者使用标志位来跟踪状态,导致代码复杂、难以维护。 而状态模式(State Pattern)可以提供一种优雅且灵活的解决方案&#…...
git常常用命令
这篇文章中,一些简单的,大家都知道的git 命令我就不再赘述,我只写出来最近在项目中常用到的一些命令。这些命令可以帮助我更好的开发。 git stash 请大家设想下面的场景,你的本地有两个分支,develop,fix分支…...
C语言中的大端字节序和小端字节序是什么?如何进行字节序的转换?
C语言中的大端字节序和小端字节序以及字节序的转换 引言 在计算机科学中,字节序是指多字节数据在存储或传输过程中字节的排列顺序。在C语言中,特别是在涉及二进制数据的处理、网络通信以及硬件相关的编程中,了解大端字节序和小端字节序的概…...
Flutter dio Http请求之Cookie管理
在应用开发过程中,我们进行Http通讯时会使用Cookie进行验证,今天我们就着重讲解Flutter 网络请求插件dio的cookie使用。 首先,我们要进行插件引用 # HTTP 请求 dio: ^5.1.1 cookie_jar: ^4.0.8 dio_cookie_manager: ^3.0.0# 获取沙盒路径 p…...
计算机网络的标准化工作及相关组织
一、国际化组织 计算机网络的标准化工作由一些主要的组织来进行管理和推动。以下是几个主要的计算机网络标准化的国际组织及其相关的标准: 1. 国际标准化组织(ISO):国际标准化组织负责制定各种行业的标准,包括计算机…...
智能座舱架构与芯片- (11) 软件篇 上
一、智能汽车基础软件平台分类 汽车软件主要分为应用软件和基础软件。应用软件和业务形态高度关联,不同控制器的应用软件之间差异较大。基础软件介于应用软件和硬件之间,用于屏蔽硬件特性、支撑应用软件。可有效地实现应用软件与硬件之间解耦࿰…...
2021秋招-算法-递归
算法-递归 教程: ⭐告别递归,谈谈我的一些经验 LeetCode刷题总结-递归篇 基础框架 leetcode刷题 1.leetcode-101. 对称二叉树-简单 101. 对称二叉树 给定一个二叉树,检查它是否是镜像对称的。 例如,二叉树 [1,2,2,3,4,4,3] 是对称的。…...
【Django-02】 Model模型和模型描述对象Meta
Model和Meta 概念ModelMetaModel支持的字段类型Meta 属性例子 概念 就是对象的意思,底层一个Model对应一张表,而Meta是Model的内部类,是用来描述Model和数据库表的相关元数据信息,比如主键,排序,unique_ke…...
【华为OD题库-030】阿里巴巴找黄金宝箱(V)-java
题目 一贫如洗的樵夫阿里巴巴在去砍柴的路上,无意中发现了强盗集团的藏宝地,藏宝地有编号从0-N的箱子,每个箱子上面贴有一个数字.阿里巴巴念出一个咒语数字k(k<N),找出连续k个宝箱数字和的最大值,并输出该最大值。 输入描述 第…...
centos7卸载mongodb数据重新安装时无法安装的问题
如果卸载不干净直接用 sudo find / -name mongo 查询所有关于mongo的文件,然后一个个去删除。 当然最好的办法还是去看日志信息。 直接去查看日志信息 sudo cat /var/log/mongodb/mongod.log 根据提示信息说这个没有权限操作 直接删除即可,都是之前…...
ES6 的 class 类和Typescript 的 class 类的区别
前言 为什么要理解ES6的类和TS类的区别: 都是面向对象的开发它们看着很像但是它们不一样学习明白了,避免混用 ES6 类是 JavaScript 中基于原型的面向对象编程的语法糖,而 TypeScript 类在此基础上增加了强类型检查和其他面向对象编程的特性…...
Android 12.0 默认授予应用权限
Android 12.0 默认授予应用权限 最近接到客户需求提到每当首次点开某个应用时都会弹出申请权限的弹窗,操作起来感觉很麻烦,需要将指定的这个应用默认授予权限,具体修改参照如下: frameworks/base/services/core/java/com/androi…...
Google Earth Engine(GEE)——多源遥感变量筛选(PCA主成分分析),变量筛选/降维处理
简介 很多时候我们需要进行数据的将为和筛选,传统的方法我们可以根绝经验方法进行筛选或者按照变量重要性和相关性进行分析,当然我们可以通过计算多个变量之间的主成分分析来进行变量的筛选,本文已森林生物量分析作为自变量,其它多源遥感变量作为相关性因变量,进行分类对…...
爬虫的http和https基础
HTTP响应状态码响应状态码 下面来看下详细的状态码数值和说明: 200系列: 200 OK:这个是最常见的,也是爬虫工程师最喜欢的,代表你本次的请求顺利拿到了响应,没有任何问题 201 Created:201代表…...
读像火箭科学家一样思考笔记05_思想实验
1. 思想实验室 1.1. 思想实验至少可以追溯到古希腊时期 1.1.1. 从那时起,它们就跨越各个学科,在哲学、物理学、生物学、经济学等领域取得重大突破 1.1.2. 它们为火箭提供动力,推翻政府,发展进化生物学,解开宇宙的奥…...
XCTF-web-easyupload
试了试php,php7,pht,phtml等,都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接,得到flag...
微信小程序之bind和catch
这两个呢,都是绑定事件用的,具体使用有些小区别。 官方文档: 事件冒泡处理不同 bind:绑定的事件会向上冒泡,即触发当前组件的事件后,还会继续触发父组件的相同事件。例如,有一个子视图绑定了b…...
C++:std::is_convertible
C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...
多场景 OkHttpClient 管理器 - Android 网络通信解决方案
下面是一个完整的 Android 实现,展示如何创建和管理多个 OkHttpClient 实例,分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...
VTK如何让部分单位不可见
最近遇到一个需求,需要让一个vtkDataSet中的部分单元不可见,查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行,是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示,主要是最后一个参数,透明度…...
【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分
一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计,提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合:各模块职责清晰,便于独立开发…...
如何理解 IP 数据报中的 TTL?
目录 前言理解 前言 面试灵魂一问:说说对 IP 数据报中 TTL 的理解?我们都知道,IP 数据报由首部和数据两部分组成,首部又分为两部分:固定部分和可变部分,共占 20 字节,而即将讨论的 TTL 就位于首…...
七、数据库的完整性
七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...
C语言中提供的第三方库之哈希表实现
一. 简介 前面一篇文章简单学习了C语言中第三方库(uthash库)提供对哈希表的操作,文章如下: C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...
人工智能--安全大模型训练计划:基于Fine-tuning + LLM Agent
安全大模型训练计划:基于Fine-tuning LLM Agent 1. 构建高质量安全数据集 目标:为安全大模型创建高质量、去偏、符合伦理的训练数据集,涵盖安全相关任务(如有害内容检测、隐私保护、道德推理等)。 1.1 数据收集 描…...
