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

c++STL-string的使用

这里写自定义目录标题

  • string的使用
    • string写成类模板的原因
    • string的版本举例
    • 构造、析构函数和赋值重载
      • 构造函数和析构函数
      • operator=
    • Iterators迭代器
      • begin和end
      • rbegin和rend
      • cbegin和cend,crbegin和crend(c++11)
    • capacity容量有关函数
      • 不同编译器下string的扩容机制
      • resize和reserve
    • Element access元素通道(例如operator[])
    • Modifiers修改用的函数
      • 尾插函数operator+=,push_back,append
      • 赋值assign
      • 插入insert、删除erase、pop_back和替换replace
        • insert、erase和pop_back
        • replace
      • 交换swap
    • string operations字符串操作
      • c_str和data
      • substr
      • find和rfind
      • 其他find
        • find_first_of
        • find_last_of
        • find_first_not_of 和find_last_not_of
    • 非成员函数
      • operator+
      • relational operators比较运算符和字典序
      • getline
    • c++11新增处理数字的函数
    • 引用计数和写时拷贝
  • 其他人眼中的string
  • 练习使用string的OJ
    • 415. 字符串相加 - 力扣
    • 917. 仅仅反转字母 - 力扣
    • 387. 字符串中的第一个唯一字符 - 力扣
    • 最后一个单词的长度
    • 125. 验证回文串 - 力扣
    • 541. 反转字符串 II - 力扣
    • 557. 反转字符串中的单词 III - 力扣
    • 43. 字符串相乘 - 力扣
    • 第一个只出现一次的字符
    • LCR 192. 把字符串转换成整数 (atoi) - 力扣
    • 344. 反转字符串 - 力扣
    • 387. 字符串中的第一个唯一字符 - 力扣

string的使用

string类用于处理字符串。

string严格来说属于标准库,因为string的产生比STL早(可以理解为STL还没出的时候string就已经被加进c++的标准库)。但string的实现和STL的工具具有很大的相似性。

string的调用需要调用头文件stringnamespace std(或加前缀std::指定域名)。

c++为了避免和c语言的库产生冲突,没有加 .h 的拓展名。

学习string时主要遵循二八原则:学习主要学习常用的,在运用熟练的过程中拓展即可。

因为计算机起源于欧美,所以从事计算机有关的工作,尽量看英文文档。

学习string主要参考string - C++ Reference。这里只做简单的翻译工作和补充。

string设置的成员函数特别多,很多都没有必要。这里写的相对详细,但很多其实没什么用,更多是翻译隔壁网站的信息。若是我的话,我会看另一篇常用的成员函数总结:string在算法竞赛中的使用_算法竞赛string-CSDN博客。

请添加图片描述

typedef basic_string<char> string表明string也是通过模板的实例化实现,但底层已经用了char类型。

尝试翻译:字符串是表示(管理)字符序列的对象。

标准字符串类为这类对象提供支持,其接口(API,个人理解是在c/c++阶段,函数的声明信息,包括给的形参、函数名和返回值)类似于标准字节容器,但添加了专门为处理单字节字符字符串而设计的特性。

字符串类是basic_string类模板的一个实例化,它使用char(即字节符)作为其字符类型,并带有默认的 char_traits 和分配器类型(有关该模板的更多信息,请参见 basic_string )。

请注意,这个类独立于所使用的编码来处理字节:如果用于处理多字节或可变长度字符(如 UTF - 8)序列,该类的所有成员(如 lengthsize)以及其迭代器,仍将以字节(而非实际编码的字符)为单位进行操作。

string用于管理字符数组,在设计时因为没有类似STL的工具作为参考,加了一百多个成员函数,而且很多函数接口显得很多余。

借助顺序表的视角看,string的底层存在3个私有变量:

char* _str;
size_t _size;
size_t _capacity;

但不一定叫这个名,取决于c++的版本。

string写成类模板的原因

日常存储字符串时,用的最多的编码是ASCII编码。这个是美国的编码,用于存储本国用到的符号或许足够。

但全球又不止美国的文字,因此为了推广,要建立二进制的值和各种符号的映射关系。

编码表:值和符号的一一映射表。比如ASCII表。

计算机起源于美国,最初用于军事。后来战争逐渐减少,为了赚钱,美国将计算机的一系列科技做成商用。

后来为了将计算机推广到全世界,在各国和美国计算机协会的互相妥协下,逐渐将标准进行拓展。这其中因为各国都开发自己的编码表,有时会造成乱码的现象。乱码即文字的存储方式和解读方式对应不上,具体详见锟斤拷�⊠是怎样炼成的——中文显示“⼊”门指南【柴知道】_哔哩哔哩_bilibili。

其中Unicode编码(万国码)取得了很大的进步。以汉语为例,2个字节足够将常见的汉字收录,太过冷门的汉字则用3个字节、4个字节来添加,所以这个可以理解为一种变长编码。

例如Linux操作系统喜欢用UTF-8(万国码的一种)。但Unicode不一定能够适合汉语,于是我国自己也搞了一套GBK编码(国标)。windows用的就有GBK。

而且不同的编码之间也可以转换。

在c++11没出现时有一种字符类型wchar_t(word char),一个wchar_t占两个字节。c++11觉得这种方式不规范,于是又给出了char16_tchar32_t

所以string这个类写成模板的形式。basic_string有4种类模板:

请添加图片描述

这里仅作为简单了解,后续有机会再讨论。

string的版本举例

目前已知的string的版本:

  • MSVC常用:
class string{
public:const static size_t npos;
private:char _buff[16];char* _str;size_t _size;size_t _capacity;
}
const size_t string::npos=-1;

这样做可以避免内存碎片的问题(比如大量定义长度小的字符串,使得内存中很多大的空间被分割成小的空间,类比的话就是河水中含有未沉底的泥沙使得水看起来不那么清澈),而且静态数组处理比动态数组快。

  1. g++,特别是Linux OS适用的g++:
class string{
public:const static size_t npos;
private:char* _str;
}
const size_t string::npos=-1;

g++的string类只有一个指针,这个指针的前段部分用于存储元素数量、容量的信息(类似c语言的柔性数组)。

构造、析构函数和赋值重载

这里主要以c++98的接口函数为例。部分情况会提及c++11。

构造函数和析构函数

string提供了这些构造函数的接口。利用这些接口可以初始化string的对象。

请添加图片描述

//用string内部的缺省值初始化string对象
string();//用str对新对象进行拷贝构造
string (const string& str);//用str的从下标pos开始的len个字符初始化新对象
string (const string& str, size_t pos, size_t len = npos);//用c风格字符串初始化新对象
string (const char* s);//用c风格字符串的前n个字符初始化新对象
string (const char* s, size_t n);//初始化对象为n个字符c
string (size_t n, char c);//通过其他对象的迭代器进行初始化
template <class InputIterator>string  (InputIterator first, InputIterator last);

npos原型是static const size_t npos = -1;,是string类的静态成员变量,它的值是-1,十六进制的补码是0xffffffff,放在size_t也就是unsigned int中时就是最大值。

这里想表示的意思是成员的缺省参数默认是最大,表示
string (const string& str, size_t pos, size_t len = npos);这个函数如果不上传len,则将新的对象用str的从pos开始的字符串初始化。

用这些接口初始化string对象:

#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS 1
#endif#include<iostream>
#include<string>
using namespace std;void f1() {//string();string st1;不建议这样写,编译器不知道// 这是函数声明还是调用构造函数//string st2();
}void f2() {//string (const char* s);string st3 = "abcdef";char st[] = { "abc12345" };string st3_1 = st, st3_2(st);//库里的类大都有将operator<<和operator>>设为友元cout << st3 << endl << st3_1 << endl << st3_2 << endl << endl;
}void f3() {string st3 = "abcdef";//string(const string & str);string st4 = st3, st5(st3);//调用拷贝构造函数cout << st4 << endl << st5 << endl << endl;
}void f4() {string st3 = "abcdef";用指定长度的字符串初始化//string (const string& str, size_t pos, size_t len = npos);string st6(st3, 2, 2);cout << st6 << endl << endl;
}void f5() {用前n个字符初始化//string (const char* s, size_t n);string st7("abcdef", 3);cout << st7 << endl << endl;
}void f6() {//string (size_t n, char c);string st8(6, 'x');cout << st8 << endl << endl;
}void f7() {string st3 = "abcdef";迭代器初始化//template <class InputIterator>//string(InputIterator first, InputIterator last);string st9(st3.begin() + 2, st3.end());cout << st9 << endl << endl;
}int main() {//f1();//f2();//f3();//f4();//f5();//f6();f7();return 0;
}

这些构造函数中最常用的还是string();string(const string& str);string(const char* s);

析构函数的作用是释放string底层向堆区申请的空间。从平时使用的角度看可不用关心底层的机制。

operator=

string对象的operator=

请添加图片描述

//将str的内容赋值给*this
string& operator= (const string& str);//将c风格字符串s的内容赋值给*this
string& operator= (const char* s);//将字符c赋值给*this
string& operator= (char c);

所以string对象还支持用单个字符赋值给string对象。

#include<iostream>
#include<string>
using namespace std;int main() {string st;st = 66;//Bcout << st << endl;st = 'c';cout << st << endl;return 0;
}

Iterators迭代器

初学的时候,迭代器可以想象为指针。用法看上去很像指针。

请添加图片描述

STL和string的迭代器都是在类中定义,所以需要加类名::

begin和end

      iterator begin();
const_iterator begin() const;iterator end();
const_iterator end() const;

beginend都有加const和不加const的两种版本,用于应对string的有关函数对形参的不同需求。

stringbeginend给的位置:
请添加图片描述

所以一般可认为迭代器是左闭右开的区间。迭代器的使用:

#include<iostream>
#include<string>
using namespace std;int main() {string st = "abcdef";string::iterator it = st.begin();while (it != st.end()) {cout << *it;string的迭代器支持++,--,和+\-整数//it++;it += 1;}cout << endl;it = st.end()-1;cout << *(st.end() - 3) << endl;while (it != st.begin()) {cout << *it;--it;}return 0;
}

通过迭代器,可以使用c++11的范围for简化枚举:

#include<iostream>
#include<string>
using namespace std;int main() {string st="abcdef";//底层实现是迭代器for (auto x : st)//x是st的单位元素的临时拷贝cout << x;cout << endl;for (char x : st)cout << x;cout << endl;for (auto &x : st)//x是st的单位元素的别名cout << x;return 0;
}

范围for循环的底层实现是迭代器,即:

for(auto it=st.begin();it!=st.end();it++){x=*it;//各种操作
}

*it赋值给x,利用x进行各种操作。

范围for要求类至少有普通版本和const版本的begin()end()(迭代器但凡更换一个名字都不支持,例如Begin,范围for就不支持),并且这些迭代器能正常访问,以及解引用(*)、前置递增(++)以及相等比较(==)和不等比较(!=)。

范围for不支持逆向枚举。可以对比迭代器的编译器转换成的的汇编语句。

个人理解,很多编译器对c++的编译行为都能通过汇编代码来了解写编译器的人的思考。

虽然string的迭代器支持用><>=<=,但最好还是用!=。因为STL的很多工具都有迭代器,那个工具在逻辑上不一定支持这些符号,比如链表list(链表结点存储不连续)。

string还可以用algorithm库的函数sort进行排序。

#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS 1
#endif#include<iostream>
#include<string>
#include<algorithm>
using namespace std;int main() {string st = "asdfghhjkl";sort(st.begin(), st.end());cout << st << endl;st = "qwertyyuiop";sort(st.begin() + 3, st.end());//指定要排列的区域cout << st << endl;return 0;
}

sort是STL的六大组件之一的算法,里面封装有很多函数。

template <class RandomAccessIterator>void sort (RandomAccessIterator first, RandomAccessIterator last);template <class RandomAccessIterator, class Compare>void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp);

RandomAccessIterator表示支持随机访问的迭代器,Compare表示完成比较工作的函数和重载有operator()并返回bool值的类。

rbegin和rend

      reverse_iterator rbegin();
const_reverse_iterator rbegin() const;reverse_iterator rend();
const_reverse_iterator rend() const;

r表示reverse,意思是这两个迭代器和beginend相反,包括迭代器加、减整数的移动方式。

请添加图片描述

#include<iostream>
#include<string>
#include<algorithm>
using namespace std;int main() {string st = "abcdef";string::reverse_iterator it = st.rbegin();while (it != st.rend()) {cout << *it;++it;}cout << endl;// e acout << *(st.rbegin() + 1) << ' ' << *(st.rend() - 1) << endl;it = st.rend()-1;while (it != st.rbegin()) {cout << *it;--it;}return 0;
}

cbegin和cend,crbegin和crend(c++11)

const_iterator cbegin() const noexcept;
const_iterator cend() const noexcept;
const_reverse_iterator crbegin() const noexcept;
const_reverse_iterator crend() const noexcept;

cbegincendcrbegincrend是c++11新增的迭代器,都是在原迭代器的基础上增加const赋予常属性。noexcept关键字用于指示函数是否可能抛出异常,在初学阶段暂时不用理会。

因为有的函数因为各种原因,形参的对象有用cosnt修饰。因此一般的迭代器无法使用,需要另外提供迭代器。

无法通过cbegincendcrbegincrend修改后台的数据,只能通过它们访问。

#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS 1
#endif#include<iostream>
#include<string>
using namespace std;int main() {string st = "abcdef";string::const_iterator it = st.cbegin();while (it != st.cend()) {cout << *it;//迭代器加了常属性,无法对底层数据进行修改//*it = '6';it += 1;}cout << endl;it = st.cend() - 1;cout << *(st.cend() - 3) << endl;while (it != st.cbegin()) {cout << *it;--it;}return 0;
}

capacity容量有关函数

请添加图片描述

函数原型:
请添加图片描述

简单介绍每个函数的用途:

size()length()

两个都是返回字符串的长度。因为string出现的比STL早,所以求长度时有length。但当STL出来的时候size用的特别多,于是string也加上了size

max_size()

早期max_size的设定是告诉用户string对象后台的字符数组最多能有多长,但实际上因为电脑一直在运行各种程序,实际内存无法达到max_size,而且max_size的返回值是固定的,这就使得这个成员函数几乎没有用途。

reserve()resize()

指定string对象要存储的数据个数。

reserve,在英语的意思是保留;reverse,在英语的意思是反转。我在使用时曾误以为reserve()的意思是字符串反转,后来发现不是。

在实际应用时,一开始确定需要的容量,可以减少扩容次数,提高程序的效率。

c++并没有规定reserve()会缩小容量。缩小容量的成本很高(比如临时开一个新空间,数据拷贝到新空间后释放旧空间),所以它们一般不会缩小容量,除非是自己写的编译器,想和别人不一样于是新加功能。

capacity()
返回当前string对象的容量,这个容量不包括\0,也就是说它返回的是当前对象存储的的字符个数。这个容量可变。

clear()
clear()用于初始化数据。stringclear并不释放空间,因为析构函数会释放。

empty()
判断string的后台数据是否为空(判断字符串是否为空)。

shrink_to_fit()
用于减小容量用于匹配当前的size,一般不会用,代价很大。

不同编译器下string的扩容机制

这是一个展示不同数据量的情况下string对象的扩容情况。

#include<iostream>
#include<string>
using namespace std;int main() {string s;size_t old = s.capacity();cout<<old<<endl;for (size_t i = 0; i < 1000; i++) {s.push_back('a');if (old != s.capacity()) {cout << s.capacity() << endl;old = s.capacity();}}return 0;
}

在MSVC编译器(cl.exe)的运行结果一般是这个(1.5倍扩容):

15
31
47
70
105
157
235
352
528
792
1188

在g++编译器(g++.exe,2倍扩容):

0
1
2
4
8
16
32
64
128
256
512
1024

这些都还不是固定的,会随着编译器的版本变化,底层的机制发生变化。

如果使用resizereserve

#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS 1
#endif#include<iostream>
#include<string>
using namespace std;int main() {string s;//s.reserve(100);s.resize(100);cout << s.capacity() << endl;return 0;
}

MSVC会多给一些,所以是111;g++则是按需给,所以输出100。

resize和reserve

它们都是改变长度。
请添加图片描述

定向扩容的函数有三个接口。只有void resize(size_t, char c);会初始化为指定字符c,除此之外都是填充\0

扩容测试案例:

#include<iostream>
using namespace std;void f1() {string s1("hello world");cout << s1 << endl;cout << s1.size() << endl;cout << s1.capacity() << endl;
}void f2() {string s1("hello world");cout << s1 << endl;cout << s1.size() << endl;cout << s1.capacity() << endl;s1.resize(13);//s1.reserve(20);//会产生扩容行为,但size并不会发生变化cout << s1 << endl;cout << s1.size() << endl;//size发生变化cout << s1.capacity() << endl;
}
void f3() {string s1("hello world");cout << s1 << endl;cout << s1.size() << endl;cout << s1.capacity() << endl;s1.resize(20, 'x');//多出来的部分用x去填充//s1.resize(2, 'x');//不会用x去更新逆向扩容后的信息cout << s1 << endl;cout << s1.size() << endl;cout << s1.capacity() << endl;
}void f4() {string s1("hello world");cout << s1 << endl;cout << s1.size() << endl;cout << s1.capacity() << endl;s1.resize(5);//s1.resize(14);cout << s1 << endl;cout << s1.size() << endl;cout << s1.capacity() << endl;
}void f5() {string s1;cout << s1.size() << endl;cout << s1.capacity() << endl;s1.reserve(20);cout << s1.size() << endl;cout << s1.capacity() << endl;
}void f6() {string s1="Hello world";cout << s1.size() << endl;cout << s1.capacity() << endl;s1.reserve(5);cout << s1.size() << endl;cout << s1.capacity() << endl;
}int main() {//f1();//使用size和capacity查看对象信息//f2();//扩容13个单位,小于原本的capacity,于是只填充\0//f3();//扩容20个单位,填充指定字符,大于capacity,所以发生类似realloc的行为f4();//逆向扩容,实际效果是容量不变,可显示字符变少(或size减少)//f5();//reserve仅扩容,不调整size//f6();//reserve在逆向扩容时会无视请求return 0;
}

扩容的机制大致可总结为:

假设sizecapacitystring的底层成员变量,用于监视数据个数和可用容量。

  • resize函数在size ≤ \leq capacity时会填充\0或指定字符,否则会对capacity进行扩容,但无论什么情况size都会发生变化。

  • reserve仅仅只是对capacity进行扩容行为,size并不会发生变化。

  • 当利用resize进行缩容时,编译器仅减少size的值,capacity并不发生变化。

  • 当利用reserve进行缩容时,reserve看上去会无视请求。

缩容需要成本。

Element access元素通道(例如operator[])

请添加图片描述

通过operator[]string对象可以像字符数组一样使用下标索引

请添加图片描述

这里用const生成operator[]的另一个重载,是因为一部分函数要求只读,另一部分要求能读能写。所以用了两个函数,加了const的那一个用于形参具有常属性的。

string因为生成临时对象会调用构造函数或析构函数,所以string对象作为实参上传时,函数的形参最好用引用,减少不必要的临时对象的生成。

atoperator[]的区别是at越界会抛异常,而operator[]会直接断言,除此之外在使用上几乎没区别。

backfront是c++11新增的内容,分别返回结尾和开头的字符。

使用案例:

#include<iostream>
#include<string>
#include<cstdio>
using namespace std;int main() {string a = "0123456789";printf("%c\n", a[3]);cout << a[4] << endl;cout << a.at(4) << endl;//at和[]是一样的cout << a.front() << ' ' << a.back() << endl;return 0;
}

Modifiers修改用的函数

请添加图片描述

尾插函数operator+=,push_back,append

函数原型:

请添加图片描述

//向*this末尾插入对象str表示的字符串
string& operator+= (const string& str);//向*this末尾插入c风格字符串s
string& operator+= (const char* s);//向*this末尾插入字符c
string& operator+= (char c);//将str的内容和*this的内容拼接在一起组成改变*this
string& operator+= (const string& str);//将c风格字符串s的内容和*this的内容拼接在一起组成改变*this
string& operator+= (const char* s);//将字符c的内容和*this的内容拼接在一起组成改变*this
string& operator+= (char c);//同operator+=
string& append (const string& str);//将str从下标subpos开始的sublen个字符插入*this的尾部
string& append (const string& str, size_t subpos, size_t sublen);//同operator+=
string& append (const char* s);//将s的前3个字符插入*this的尾部
string& append (const char* s, size_t n);//将n个字符c插入*this的尾部
string& append (size_t n, char c);//通过迭代器指定某一片段插入*this
template <class InputIterator>string& append (InputIterator first, InputIterator last);

三个函数的功能都是将两个部分拼接成一个字符串。因为历史原因,这里设计的很多。实际应用最多的还是operator+=

#include<iostream>
#include<string>
using namespace std;void f1() {string ss;ss.push_back('a');ss.push_back('b');cout << ss;
}void f2() {string st("abcd");string st2("efg");//string& append (const string& str);st.append(st2);cout << st << endl;//string& append(const string & str, size_t subpos, size_t sublen);st.append(st2, 1, 1);cout << st << endl;//string& append (const char* s);char ch[] = { "bcdef" };string st3 = "a";cout << st3.append(ch) << endl << st3 << endl;//string& append (const char* s, size_t n);st3.append(ch, 3);cout << st3 << endl;//string& append(size_t n, char c);string st4 = "ab";st4.append(4, 'x');cout << st4 << endl;//template <class InputIterator>//  string& append(InputIterator first, InputIterator last);st4.append(st.begin(), st.end());cout << st4 << endl;
}void f3() {string st = "abc";//string& operator+= (char c);st.operator+=('d');st += 'e';//string& operator+= (const char* s);st += "fg";//string& operator+= (const string & str);string st2 = "hi";st += st2;cout << st << endl;
}int main() {//f1();//push_back//f2();//appendf3();//operator+=return 0;
}

赋值assign

请添加图片描述

如同标题所言,assign函数有赋值的功能。但平常使用肯定是用赋值重载operator=。所以这个函数的功能和另一个更好用的函数重叠,造成的结果是几乎用不上。

形参的内容也是相差不多,这里简单贴一个测试案例,然后忘了这个函数,用更舒服的operator=

#include<iostream>
#include<string>int main() {std::string str("xxxxxxx");std::string base = "The quick brown fox jumps over a lazy dog.";str.assign(base);std::cout << str << '\n';str.assign(base, 5, 10);std::cout << str << '\n';return 0;
}

插入insert、删除erase、pop_back和替换replace

insert、erase和pop_back

请添加图片描述

insert/erase/repalce能不用就尽量不用,因为他们都涉及挪动数据,效率不高,而且接口设计复杂繁多,需要时查一下文档即可。

这里给个案例简单测试基本使用。

#include<iostream>
#include<string>
using namespace std;void f1() {string st("hello world");string st2("abc");//string& insert (size_t pos, const string& str);st.insert(0, st2);cout << st << endl;
}void f2() {string st("hello world");string st2("abc");//string& insert (size_t pos, const string& str, size_t subpos, size_t sublen);st.insert(1, st2, 1, 2);cout << st << endl;
}void f3() {string st("hello world");string st2("abc");//string& insert (size_t pos, const char* s);st.insert(5, "xxx");//在下标5和下标4之间插入字符串xxxcout << st << endl;}void f4() {string st("hello world");string st2("abc");//string& insert (size_t pos, const char* s, size_t n);st.insert(7, "636666", 4);cout << st << endl;
}void f5() {string st("hello world");//string& insert (size_t pos, size_t n, char c);cout << st.insert(4, 4, '#') << endl;
}void f6() {string st("hello world");//void insert(iterator p, size_t n, char c);//c++98有的,插入n个字符cst.insert(st.begin() + 2, 4, '+');cout << st << endl;
}void f7() {string st("hello world");//iterator insert (const_iterator p, size_t n, char c);//c++11新增的//旧版本的Devc++5.11无法使用完整的c++11,因此可能无法使用 cout << st.insert(st.cbegin() + 2, 4, '+')-st.begin() << endl;cout << st << endl;
}void f8() {string st("hello world");string st2("abc");//iterator insert (iterator p, char c);//在指定迭代器之前插入字符c并返回被插入的字符c所在的位置的迭代器cout << *st.insert(st.begin() + 2, '-') << endl;cout << st << endl;
}void f9() {string st("hello world");string st2("abc");//template <class InputIterator>//  void insert(iterator p, InputIterator first, InputIterator last);//在指定迭代器处插入迭代器表示的范围//反向迭代器表示逆向插入st.insert(st.begin() + 2, st2.rbegin(), st2.rend());cout << st << endl;
}void f10() {string st("hello world");//string& erase (size_t pos = 0, size_t len = npos);//删除从下标pos开始的len个字符,并返回整个字符串的引用。//len默认为-1,表示一直到结尾cout << st.erase(2, 3) << endl;
}void f11() {string st("hello world");//iterator erase (iterator p);//删除指定迭代器的字符并返回p处的迭代器cout << *st.erase(st.begin() + 7) << endl;cout << st << endl;
}void f12() {string st("hello world");//iterator erase (iterator first, iterator last);//删除指定区域后拼接,并返回first处的迭代器cout << *st.erase(st.begin() + 3, st.begin() + 6)<<endl;cout << st << endl;
}int main() {//f1();//f2();//f3();//f4();//f5();//f6();f7();//f8();//f9();//f10();//f11();//f12();return 0;
}

c++11新增pop_back函数用于弹出字符串的结尾。

replace

replace用于替换指定片段的字符为新的字符。

请添加图片描述

根据上文的经验,这里的含义也大都能猜出来,这里省略若干个使用示例。

#include<iostream>
#include<string>
using namespace std;int main() {//替换从下标5开始的3个字符//替换的数据相同的话还好,但凡多一个或少一个//都得移动字符string s1("hello world");s1.replace(5, 3, "]]20");cout << s1 << endl;return 0;
}

如果涉及多个地方的替换,可以用这种空间换时间的方式:

#include<iostream>
#include<string>
using namespace std;int main() {// 空格替换为20%//比replace高效的替换方式string s2("The quick brown fox jumps over a lazy dog.");string s3;for (auto ch : s2) if (ch != ' ') s3 += ch;elses3 += "20%";return 0;
}

交换swap

在命名空间std中还有一个函数模板swap

请添加图片描述

这个函数模板会生成临时对象,会生成3次深拷贝(一次构造,两次赋值)。

string自带的swap则会交换string底层字符数组的地址。所以更建议用成员函数swap

#include<iostream>
#include<string>
using namespace std;int main() {string s2("The quick brown fox jumps over a lazy dog.");string s3("The20%quick20%brown20%fox20%jumps20%over20%a20%lazy20%dog.");//s2 = s3;//s2.assign(s3);//swap(s2, s3);s2.swap(s3);//严格来说都不如这个高效cout << s3;return 0;
}

可通过查看c_str()直观感受地址交换。

string还有一个现成的非成员函数swap,有现成的swap就不会生成函数模板,以至于string永远不会调用函数模板,所以看起来即使是用void swap(string&,string&);,产生的效果也和void swap(string&);一样。

请添加图片描述

#include<iostream>
#include<string>
using namespace std;int main() {string s2("The quick brown fox jumps over a lazy dog.");string s3("The20%quick20%brown20%fox20%jumps20%over20%a20%lazy20%dog.");printf("%p\n", s2.c_str());printf("%p\n", s3.c_str());//s2.swap(s3);swap(s2, s3);//看上去两个似乎没区别printf("%p\n", s2.c_str());printf("%p\n", s3.c_str());return 0;
}

输出结果之一:

0148C850
01490AB8
01490AB8
0148C850

string operations字符串操作

请添加图片描述

copy

size_t copy (char* s, size_t len, size_t pos = 0) const;,将string对象的内容拷贝给c语言风格的字符数组s,返回拷贝的值。几乎不用。

compare用于比较string对象和字符串之间的字典序。这个由比较运算符重载就能完成,因此几乎不用。

get_allocator() 主要用于获取 string 对象所使用的分配器(或者说内存)。在初学时用不上,后续会补充。

c_str和data

c++毕竟是建立在c语言的基础之上,很多c语言的东西string类也要支持。比如c语言的函数fopen,文件名只能用c语言的字符串,比如mysql只提供c语言的接口。

所以string额外提供了c_str()返回c风格的字符串数组的首指针。

尽管可以通过c_str修改string的后台数据,但不会调用构造函数和析构函数,很容易发生错误。

请添加图片描述

datac_str是一样的,但出于历史原因,c_str被保留。

substr

substr用于获取string对象中的一部分作为子串。

函数原型:

string substr (size_t pos = 0, size_t len = npos)const;

使用案例:

#include<iostream>
#include<string>
using namespace std;int main() {string s1("test.cpp.tar.zip");cout << s1.substr(5, 3) << endl;//分离出cppcout << s1.substr(5) << endl;//切除前5个字符后剩下的字符return 0;
}

find和rfind

find系列函数大致用于在主串中寻找匹配串出现的位置。rfindfind功能差不多,但不同的是从字符串末尾(迭代器end的前一个字符)往字符串开头找。

比如主串ababacb中,匹配串bac出现的位置是下标3开始的3个字符。

请添加图片描述

findrfind案例:

#include<iostream>
#include<string>
using namespace std;int main() {string s1("test.cpp.tar.zip");size_t i = s1.find('.');//返回第一次找到的位置的下标,否则返回npos(-1)cout << i << endl;i = s1.rfind('.');//和find相比,逆向寻找cout << i << endl;string s2 = s1.substr(i);//切除前size()-i个字符后将子串赋值给s2cout << s2 << endl;return 0;
}

应用之一:分离网站成分。

网站大都分几个部分:协议(https)、域名(什么什么.com,例如legacy.cplusplus.com)、资源名(例如:reference/string/string/substr)。

计算机想要连接互联网(Internet),需要遵守别人划定的规定(指TCP/IP协议,当然有其他的),也可以自己搭建网络,自己写协议,让自己那个圈子的人连接。

#include<iostream>
#include<string>
using namespace std;int main() {//分离网址的成分//string s3("https://legacy.cplusplus.com/reference/string/string/rfind/");string s3("ftp://www.baidu.com/?tn=65081411_1_oem_dg");string sub1, sub2, sub3;size_t i1 = s3.find(':');if (i1 != string::npos)//npos实际是-1,用无符号整数存储是最大的数,正常字符串不会这么长sub1 = s3.substr(0, i1);elsecout << "没有找到i1" << endl;size_t i2 = s3.find('/', i1 + 3);//跳过://if (i2 != string::npos)sub2 = s3.substr(i1 + 3, i2 - (i1 + 3));elsecout << "没有找到i2" << endl;sub3 = s3.substr(i2 + 1);cout << sub1 << endl;cout << sub2 << endl;cout << sub3 << endl;return 0;
}

其他find

其他findfind_first_offind_last_offind_first_not_offind_last_not_of都是查找对象中,形参内的字符的位置。

但这个查找可以用计数排序(或哈希表)的思路代替。比如开所有字符的计数数组vis[256],将匹配串中的字符标记,之后再遍历主串,凡是找到被vis标记过的字符就记录下标,这样时间复杂度可以降到 O ( m + n ) O(m+n) O(m+n)

也就是说,这些看上去和find有关的函数算是历史遗留的史(bushi)。

find_first_of
size_t find_first_of (const string& str, size_t pos = 0) const;
size_t find_first_of (const char* s, size_t pos = 0) const;
size_t find_first_of (const char* s, size_t pos, size_t n) const;	
size_t find_first_of (char c, size_t pos = 0) const;

find_first_of,是底层标记匹配串strs(或字符c)在主串中pos后出现的位置的位置,一般需要用一个变量来接收。

从名字看不出来这个函数到底有啥用,算是设计得很失败的那几个。

而且平时也不怎么用的上,遇到了就查文档。所以学的时候有个印象即可。

例如,找到{a,e,i,o,u}的其中之一后换成*

#include<iostream>
#include<string>
using namespace std;int main() {std::string str("Please, replace the vowels in this sentence by asterisks.");//找到{a,e,i,o,u}在str中第1个出现的位置。std::size_t found = str.find_first_of("aeiou");while (found != std::string::npos){str[found] = '*';found = str.find_first_of("aeiou", found + 1);}std::cout << str << '\n';return 0;
}

输出:

Pl**s*, r*pl*c* th* v*w*ls *n th*s s*nt*nc* by *st*r*sks.
find_last_of
size_t find_last_of (const string& str, size_t pos = npos) const;
size_t find_last_of (const char* s, size_t pos = npos) const;
size_t find_last_of (const char* s, size_t pos, size_t n) const;
size_t find_last_of (char c, size_t pos = npos) const;

find_last_offind_last_of的区别是从尾迭代器开始逆向查找。

// string::find_last_of
#include <iostream>       // std::cout
#include <string>         // std::string
#include <cstddef>         // std::size_tvoid SplitFilename(const std::string& str)
{//输出待分隔开的字符串std::cout << "Splitting: " << str << '\n';//从后往前查找字符串str中出现\或/出现的位置std::size_t found = str.find_last_of("/\\");std::cout << " path: " << str.substr(0, found) << '\n';std::cout << " file: " << str.substr(found + 1) << '\n';
}int main()
{std::string str1("/usr/bin/man");std::string str2("c:\\windows\\winhelp.exe");SplitFilename(str1);SplitFilename(str2);return 0;
}

输出:

Splitting: /usr/bin/manpath: /usr/binfile: man
Splitting: c:\windows\winhelp.exepath: c:\windowsfile: winhelp.exe
find_first_not_of 和find_last_not_of
size_t find_first_not_of (const string& str, size_t pos = 0) const;
size_t find_first_not_of (const char* s, size_t pos = 0) const;
size_t find_first_not_of (const char* s, size_t pos, size_t n) const;
size_t find_first_not_of (char c, size_t pos = 0) const;size_t find_last_not_of (const string& str, size_t pos = npos) const;
size_t find_last_not_of (const char* s, size_t pos = npos) const;
size_t find_last_not_of (const char* s, size_t pos, size_t n) const;
size_t find_last_not_of (char c, size_t pos = npos) const;

这2个和不加not的函数完全相反,是只保留形参内的字符。

#include<iostream>
#include<string>
using namespace std;int main() {std::string str("Please, replace the vowels in this sentence by asterisks.");//找到不属于集合{a,e,i,o,u}的字符,在str中第1个出现的位置。std::size_t found = str.find_first_not_of("aeiou");while (found != std::string::npos){str[found] = '*';found = str.find_first_not_of("aeiou", found + 1);}std::cout << str << '\n';return 0;
}

输出:

**ea*e***e**a*e***e**o*e***i****i***e**e**e****a**e*i****

非成员函数

请添加图片描述

其中流插入>>和流提取<<支持cincoutstring的对象进行操作。

通过cin>>可以对string对象输入字符串,但不接受空格和换行符。

通过cout<<可以将字符串输出。

operator+

根据类和对象——运算符重载-CSDN博客,operator+返回临时对象的拷贝,而operator+=会对原本的对象进行修改。这两个加在string的含义是拼接字符串组成新的字符串。

请添加图片描述

operator+不属于string的成员函数,是为了确保顺序。和operator<<operator>>一样是string的友元函数。

请添加图片描述

operator+因为传值返回,会调用很多可优化的拷贝构造,不建议多用,而是更建议用operator+=

relational operators比较运算符和字典序

字典序解释:2个字符串s1s2,从前向后一个一个字符进行比较。遇到第i个不相同时,结果就是s1[i]s2[i]的大小关系。如果一直相等,有一个字符串结束,则长的大,一样长的话,则2个字符串相等。

字典序比较有两种方式。

  1. 比较运算符:<<===>=>!=。比如:
if(s1>s2)cout<<s1;
elsecout<<s2;
  1. compare()函数,最常见的方式是s1.compare(s2)s1s2相等时返回0;s1字典序小于s2时返回值小于0;s1字典序大于s2时返回值大于0。

只看c++98的话,比较运算符的形参都差不多,只需保证至少1个形参是string的对象即可。

请添加图片描述

其中整数的比较和字典序相比不同的是,长度长的数字大,一样长则逐一比较,直到第一个不相同的数字,它们的大小关系就是两个大整数的大小关系。

getline

//获取字符串直到读取到字符delim
istream& getline (istream& is, string& str, char delim);
//获取一行字符串
istream& getline (istream& is, string& str);

使用示例:

#include<iostream>
#include<string>
using namespace std;void f1() {string st;getline(cin, st);cout << st << endl;
}void f2() {string st;getline(cin, st, 'p');cout << st << endl;
}void f3() {string st;int n;cin >> n;//这里会留下一个换行符,使st无法读取数据getline(cin, st);cout << st << endl;
}void f4() {string st;int n;(cin >> n).get();//用get()吸收//getline(cin, st);//多次重复读取//getchar();//用getchar吸收getline(cin, st);cout << st << endl;
}int main() {//f1();//读取指定字符停下//f2();//读取一行//f3();//getline和cin搭配时的读取异常f4();//getline和cin搭配时的读取异常解决方案return 0;
}

c++11新增处理数字的函数

这里只列举basic_string<char>使用的。

//获取str中的浮点数
double stod (const string&  str, size_t* idx = 0);
float stof (const string&  str, size_t* idx = 0);
long double stold (const string&  str, size_t* idx = 0);//获取str中的整数,默认转换为10进制
int stoi (const string&  str, size_t* idx = 0, int base = 10);
long stol (const string&  str, size_t* idx = 0, int base = 10);long long stoll (const string&  str, size_t* idx = 0, int base = 10);
unsigned long stoul (const string&  str, size_t* idx = 0, int base = 10);
unsigned long long stoull (const string&  str, size_t* idx = 0, int base = 10);//将数字转换为string对象
string to_string (int val);
string to_string (long val);
string to_string (long long val);
string to_string (unsigned val);
string to_string (unsigned long val);
string to_string (unsigned long long val);
string to_string (float val);
string to_string (double val);
string to_string (long double val);

应用举例(浮点数):

#include <iostream>
#include <string>
using namespace std;int main() {string st="3.14159";size_t i=0;cout<<stod(st)<<endl; cout<<stod(st,&i)<<endl;cout<<i<<endl<<endl;st="3.14159abcdef";cout<<stod(st)<<endl; cout<<stod(st,&i)<<endl;cout<<i<<endl<<endl;st="3.14cdef159ab";cout<<stod(st)<<endl; cout<<stod(st,&i)<<endl;cout<<i<<endl<<endl;st="3.cdef14159ab";cout<<stod(st)<<endl; cout<<stod(st,&i)<<endl;cout<<i<<endl<<endl;st="3cdef.14159ab";cout<<stod(st)<<endl; cout<<stod(st,&i)<<endl;cout<<i<<endl<<endl;//	st="c3def.14159ab";//要以数字开头 
//	cout<<stod(st)<<endl; st="3.141592653589793";cout<<stod(st)<<endl; cout<<stod(st,&i)<<endl;cout<<i<<endl;return 0;
}

应用举例(整数):

#include <iostream>
#include <string>
#include <cstdio>
using namespace std;int main() {string st="3a3f3f3f";size_t i=0;printf("%d\n",stoi(st,&i,11));//3+10*11+3*121=3+110+363=476cout<<i<<endl<<endl;printf("%x\n",stoi(st,&i,16));cout<<i<<endl<<endl;printf("%d\n",stoi(st,&i,8));cout<<i<<endl<<endl;return 0;
}

应用举例(to_string):

#include <iostream>
#include <string>
using namespace std;int main() {int int_max=2e32-1;string Int_max=to_string(int_max);cout<<Int_max<<endl;return 0;
}

引用计数和写时拷贝

引用计数用来记录资源使用者的个数,可以理解为编译器在编译代码时额外开一个变量进行计数。

写时拷贝可以先看C++ STL string的Copy-On-Write(写时拷贝)技术 | 酷 壳 - CoolShell,这里只做简单解释。

在构造时,将资源的计数给成1,每增加一个对象使用该资源,就给计数增加1,当某个对象被销毁时,先给该计数减1,然后再检查是否需要释放资源,如果计数为1,说明该对象时资源的最后一个使用者,将该资源释放;否则就不能释放,因为还有其他对象在使用该资源。

引用计数在string中的应用是优化浅拷贝问题。

浅拷贝问题归纳起来就是2点:

  1. 析构函数调用2次。若析构函数存在释放空间的行为,则会产生重复释放的情况。
  2. 修改一个对象对另一个对象造成影响。

优化即多个对象如果在不修改的情况下,它们可以共同管理堆区中的同一个字符串数组

当其中一个对象销毁时,空间不会立即被释放,而是根据引用计数变量来判断是否需要释放申请的空间。

若计数变量变成0,说明已经没有对象使用这片空间,可以释放空间。

写时拷贝本质就是一种延迟拷贝,即我可以现在拷贝,但我为了效率选择不拷贝,用尽可能少的空间创造最大的价值。

某个对象要修改字符串数组,则另外申请新的空间进行修改,这就是写时拷贝(全称是引用计数写时拷贝)。

请添加图片描述

windows OS下的MSVC(Visual Studio 2019)的string类是直接拷贝(据说Visual Studio 2022和最新版也引入了引用计数和写时拷贝,但我没用过也就没测试),Linux 操作系统下的g++则是引用计数和写时拷贝。

在操作系统中也有子进程和主进程共用同一进程空间的情况,这也是引用计数作为一种优化方案的应用之一。

至于引用计数写时拷贝怎么实现,可以参考c++另一个重要的概念:智能指针

其他人眼中的string

这里贴一些链接。毕竟自己也是站在巨人的肩膀上。

C++的std::string的“读时也拷贝”技术! | 酷 壳 - CoolShell

C++面试中string类的一种正确写法 | 酷 壳 - CoolShell

STL 的string类怎么啦?_string 截取复杂度 c+±CSDN博客

练习使用string的OJ

415. 字符串相加 - 力扣

415. 字符串相加 - 力扣(LeetCode)

高精度加法。参考程序:

class Solution {
public:string addStrings(string num1, string num2) {if(num1.size()<num2.size())num1=string(num2.size()-num1.size(),'0')+num1;if(num1.size()>num2.size())num2=string(num1.size()-num2.size(),'0')+num2;for(size_t i=num1.size()-1,w=0,tmp;i!=-1;i--){tmp=(num1[i]-'0')+(num2[i]-'0')+w;w=tmp/10;tmp%=10;num1[i]=char(tmp+'0');if(i==0&&w)num1=to_string(w)+num1;}return num1;}
};

917. 仅仅反转字母 - 力扣

917. 仅仅反转字母 - 力扣(LeetCode)

双指针模拟,每次交换都连续移动直到找到下一对字母。

class Solution {
public:string reverseOnlyLetters(string s) {if(s.empty())return s;int l=0,r=s.size()-1;while(l<s.size()&&!isalpha(s[l]))l++;while(r<s.size()&&!isalpha(s[r]))r--;while(l<r){swap(s[l++],s[r--]);while(l<s.size()&&!isalpha(s[l]))l++;while(r<s.size()&&!isalpha(s[r]))r--;}return s;}
};

387. 字符串中的第一个唯一字符 - 力扣

387. 字符串中的第一个唯一字符 - 力扣(LeetCode)

计数排序。

class Solution {
public:int firstUniqChar(string s) {int a[26]={0};for(auto&x:s)a[x-'a']++;for(size_t i=0;i<s.size();i++){if(a[s[i]-'a']==1)return i;}return -1;}
};

最后一个单词的长度

字符串最后一个单词的长度_牛客题霸_牛客网

连续输入单词即可。

#include <iostream>
using namespace std;int main() {string st;size_t ans;while(cin>>st)ans=st.size();cout<<ans;return 0;
}

125. 验证回文串 - 力扣

125. 验证回文串 - 力扣(LeetCode)

class Solution {
public:bool isPalindrome(string s) {string st;for(auto&x:s){if((x>='A'&&x<='Z')||(x>='a'&&x<='z')||(x>='0'&&x<='9')){if(x<'a')st+=char(x+32);elsest+=x;}}int l=0,r=st.size()-1;while(l<r){if(st[l++]!=st[r--])return 0;}return 1;}
};

541. 反转字符串 II - 力扣

541. 反转字符串 II - 力扣(LeetCode)

将字符串每k个一组分别存放,之后对符合要求的组进行逆序处理,最后再拼接即可。

class Solution {
public:string reverseStr(string s, int k) {if(s.size()<k){int l=0,r=s.size()-1;while(l<r)swap(s[l++],s[r--]);return s;}else if(s.size()<2*k){int l=0,r=k-1;while(l<r)swap(s[l++],s[r--]);return s;}vector<string>tmp(1,"");int p=0;for(size_t i=0;i<s.size();i++){if(tmp[p].size()<k)tmp[p].push_back(s[i]);else{if(p%2==0){int l=0,r=k-1;while(l<r)swap(tmp[p][l++],tmp[p][r--]);}tmp.push_back("");++p;tmp[p].push_back(s[i]);}}if(p%2==0){int l=0,r=tmp[p].size()-1;while(l<r)swap(tmp[p][l++],tmp[p][r--]);}s.clear();for(auto&x:tmp)s+=x;return s;}
};

557. 反转字符串中的单词 III - 力扣

557. 反转字符串中的单词 III - 力扣(LeetCode)

class Solution {
public:string reverseWords(string s) {size_t l=0,r=0;for(size_t i=0;i<s.size();i++){if(s[i]==' '){r=i-1;while(l<r)swap(s[l++],s[r--]);l=i+1;}}r=s.size()-1;while(l<r)swap(s[l++],s[r--]);return s;}
};

43. 字符串相乘 - 力扣

43. 字符串相乘 - 力扣(LeetCode)

高精度乘法。

class Solution {
public:string multiply(string num1, string num2) {string &a=num1,&b=num2,c(a.size()+b.size(),'0');for(size_t j=b.size()-1,w=0,tmp;j!=-1;j--){w=0;for(size_t i=a.size()-1;i!=-1;i--){tmp=(c[i+j+1]-'0')+(a[i]-'0')*(b[j]-'0')+w;w=tmp/10;tmp%=10;c[i+j+1]=tmp+'0';}c[j]+=w;}while(c.size()>1&&c[0]=='0')c.erase(0,1);return c;}
};

第一个只出现一次的字符

找出字符串中第一个只出现一次的字符_牛客题霸_牛客网

#include <bits/stdc++.h>
using namespace std;int main() {int a[26]={0};string st;cin>>st;for(auto&x:st)a[x-'a']++;for(auto&x:st)if(a[x-'a']==1){cout<<x;return 0;}cout<<-1;return 0;
}
// 64 位输出请用 printf("%lld")

LCR 192. 把字符串转换成整数 (atoi) - 力扣

LCR 192. 把字符串转换成整数 (atoi) - 力扣(LeetCode)

首先清理在前缀的空格,然后判断符号并标记。

之后枚举每个字符,是数字就统计,不是就停止,就这样截取11位。

若真截取到11位,则根据是否是负数返回-unsigned(-1)2e32-1

若只截取到10位,和2e32-1做比较,大于的话则根据是否是负数返回-unsigned(-1)2e32-1

否则转换成数字再返回。

class Solution {
public:int myAtoi(string str) {size_t p=0;while(p<str.size()&&str[p]==' ')++p;bool flag=0;if(str[p]=='+')++p;else if(str[p]=='-'){++p;flag=1;}else if(str[p]<'0'||str[p]>'9'){return 0;}string ans;while(ans.size()<11&&p<str.size()&&str[p]>='0'&&str[p]<='9'){ans+=str[p++];while(ans.size()>1&&ans[0]=='0')ans.erase(0,1);}if(ans.size()==11){if(flag)return -2147483648;elsereturn 2147483647;}if(ans.size()==10&&ans>"2147483647"){if(flag)return -2147483648;elsereturn 2147483647;}int num=0;for(auto&x:ans)num=num*10+(x-'0');if(flag)return -num;elsereturn num;}
};

344. 反转字符串 - 力扣

344. 反转字符串 - 力扣(LeetCode)

无脑双指针。

class Solution {
public:void reverseString(vector<char>& s) {int l=0,r=s.size()-1;while(l<r){swap(s[l++],s[r--]);}}
};

387. 字符串中的第一个唯一字符 - 力扣

387. 字符串中的第一个唯一字符 - 力扣(LeetCode)

计数排序。

class Solution {
public:int firstUniqChar(string s) {int a[26]={0};for(auto&x:s)a[x-'a']++;for(size_t i=0;i<s.size();i++){if(a[s[i]-'a']==1)return i;}return -1;}
};

相关文章:

c++STL-string的使用

这里写自定义目录标题 string的使用string写成类模板的原因string的版本举例构造、析构函数和赋值重载构造函数和析构函数operator Iterators迭代器begin和endrbegin和rendcbegin和cend&#xff0c;crbegin和crend&#xff08;c11&#xff09; capacity容量有关函数不同编译器下…...

总结C/C++中程序内存区域划分

C/C程序内存分配的⼏个区域 1..栈区&#xff08;stack&#xff09;&#xff1a;在执⾏函数时&#xff0c;函数内局部变量的存储单元都可以在栈上创建&#xff0c;函数执⾏结束时 这些存储单元⾃动被释放。栈内存分配运算内置于处理器的指令集中&#xff0c;效率很⾼&#xff0c…...

C# 方法(方法重载)

本章内容: 方法的结构 方法体内部的代码执行 局部变量 局部常量 控制流 方法调用 返回值 返回语句和void方法 局部函数 参数 值参数 引用参数 引用类型作为值参数和引用参数 输出参数 参数数组 参数类型总结 方法重载 命名参数 可选参数 栈帧 递归 方法重载 一个类中可以有多个…...

Dockerfile 完全指南:从入门到最佳实践

Dockerfile 完全指南&#xff1a;从入门到最佳实践 1. Dockerfile 简介与作用 Dockerfile 是一个文本文件&#xff0c;包含了一系列用于构建 Docker 镜像的指令。它允许开发者通过简单的指令定义镜像的构建过程&#xff0c;实现自动化、可重复的镜像构建。 主要作用&#xf…...

DEEPPOLAR:通过深度学习发明非线性大核极坐标码(2)

目录 2.问题的提出和背景 2.1 信道编码 2.2.极化码 极坐标编码 极坐标解码 原文&#xff1a;《DEEPPOLAR: Inventing Nonlinear Large-Kernel Polar Codes via Deep Learning》 2.问题的提出和背景 2.1 信道编码 信道编码是一种为传输添加冗余的技术&#xff0c;使其对…...

ESP32-S3 学习笔记(1)

ESP32-S3 学习笔记&#xff08;1&#xff09; 背景环境添加工程文件材料准备轻触开关的正负极 背景 ​ 闲来无事&#xff0c;看到立创论坛上有许多大佬开源的项目&#xff0c;甚是厉害&#xff0c;于是决定自己也来搞一搞&#xff0c;同时可以做一些技术积累&#xff0c;看了很…...

Python Cookbook-7.9 访问 MySQL 数据库

任务 想访问一个 MySQL 数据库。 解决方案 MySQLdb 模块正是为这种任务而设计的: import MySQLdb #创建一个连接对象,再用它创建游标 con = MySQLdb.connect(host = "127.0.0.1", port = 3306, user = "joe",<...

docker安装superset实践

1、拉取docker镜像 docker pull apache/superset:latest 2、安装superset容器 mkdir /usr/local/develop/docker/superset/ -p touch /usr/local/develop/docker/superset/superset_config.py superset_config.py配置文件如下&#xff1a; SQLALCHEMY_DATABASE_URI mysql:…...

Web开发—Vue工程化

文章目录 前言 Vue工程化 一、介绍 二、环境准备 1.介绍create-vue 2.NodeJS安装 3.npm介绍 三&#xff0c;Vue项目创建 四&#xff0c;项目结构 五&#xff0c;启动项目 六&#xff0c;Vue项目开发流程 七&#xff0c;API风格 前言 Vue工程化 前面我们在介绍Vue的时候&#…...

什么是硬件中断请求号?什么是中断向量号?

一、硬件中断请求号&#xff08;IRQ&#xff0c;Interrupt Request Number&#xff09; ​定义​&#xff1a; 硬件中断请求号&#xff08;IRQ&#xff09;是硬件设备向CPU发送中断请求时使用的唯一标识符&#xff0c;用于区分不同的中断源。例如&#xff0c;键盘、硬盘等外设…...

[Java实战]Spring Boot 定时任务(十五)

[Java实战]Spring Boot 定时任务&#xff08;十五&#xff09; 一、定时任务的应用场景 数据同步&#xff1a;每日凌晨同步第三方数据状态检查&#xff1a;每5分钟扫描订单超时未支付资源清理&#xff1a;每小时清理临时文件报表生成&#xff1a;每月1号生成财务统计报表通知…...

OpenWrt开发第7篇:OpenWrt配置支持Web界面

文/指尖动听知识库-谷谷 文章为付费内容,商业行为,禁止私自转载及抄袭,违者必究!!! 文章专栏:Openwrt开发-基于Raspberry Pi 4B开发板 OpenWrt的luci是一个基于Web的图形化管理界面,为用户提供了直观的操作方式,无需命令行即可完成大部分功能的配置。 1.在终端输入ma…...

【多模态】IMAGEBIND论文阅读

every blog every motto: Although the world is full of suffering&#xff0c; it is full also of the overcoming of it 0. 前言 IMAGEBIND 多模态论文梗概 IMAGEBIND是一种夸模态的神经网络&#xff0c;以图片为中心&#xff0c;联合六中模态的网络&#xff08;图片、文…...

【C语言干货】二维数组传参本质

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、二维数组的内存布局 1.二维数组的实质2.二维数组的地址关系 二、二维数组传参的本质 1.参数传递的退化机制2.三种等效的函数声明方式 总结 前言 提示&#…...

基于SpringBoot的抽奖系统测试报告

一、编写目的 本报告为抽奖系统测试报告&#xff0c;本项目可用于团体抽奖活动&#xff0c;包括了用户注册&#xff0c;用户登录&#xff0c;修改奖项以及抽奖等功能。 二、项目背景 抽奖系统采用前后端分离的方法来实现&#xff0c;同时使用了数据库来存储相关的数据&…...

Go语言从零构建SQL数据库(9)-数据库优化器的双剑客

数据库优化器的双剑客&#xff1a;谓词下推与列裁剪 在数据库查询优化的世界里&#xff0c;有两位特别重要的"超级英雄"&#xff1a;谓词下推和列裁剪。这两种优化技术虽然简单&#xff0c;却能带来惊人的性能提升。今天&#xff0c;我们就来揭开它们的神秘面纱&…...

C++中什么是函数指针?

在C中&#xff0c;函数指针是一个指向函数的指针变量。通过函数指针&#xff0c;我们可以像使用函数一样调用它所指向的函数&#xff0c;常用于实现回调函数、函数指针数组等功能。 以下是一个简单的C代码示例&#xff0c;展示了函数指针的使用&#xff1a; cpp #include <…...

Python工具链UV整合环境管理

Python工具链UV整合环境管理 终极Python工具链UV&#xff1a;从依赖管理到项目开发的全维度解析一、引言&#xff1a;重新定义Python开发的大一统时代二、深度安装指南&#xff1a;多场景适配方案1. 官方独立安装器&#xff08;推荐方案&#xff09;2. 进阶安装方式3. 安装验证…...

RuoYi-v4.7.8 jar/war部署

准备条件 jdk-8u73-windows-x64.exe mysql5.7 apache-tomcat-9.0.60 apache-maven-3.8.1 RuoYi-v4.7.8.zip &#xff08;官网 RuoYi&#xff09; 登录gitee,选择标签要下载的版本好&#xff0c;点击克隆下载zip压缩文件 安装maven Apache Archive Distribution Directory…...

基于SpringBoot的小区停车位管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;…...

张量并行优质博客

必读图解系列1 比较全面的相关文献总结博客&#xff0c;可以重点看一下其中的行列切分算子2 # 图解大模型训练之&#xff1a;张量模型并行(TP)&#xff0c;Megatron-LM ↩︎ # 大规模分布式 AI 模型训练系列——张量并行 ↩︎...

汽车诊断简介

历史 20世纪80年代&#xff0c;由于美国西海岸严重的雾霾问题&#xff0c;CARB&#xff08;加州空气资源委员会&#xff09;通过了一项法律&#xff0c;要求对机动车辆进行车载监测诊断。这推动了OBD-I的引入&#xff0c;并在1990年代被OBD II取代。与此同时&#xff0c;欧洲也…...

suricata之规则去重

一、环境和背景 1.1 环境 OS: Ubuntu 22.04.5 LTS IDE: vscode suricata: suricata 7.0.5 1.2 背景 在添加规则时&#xff0c;为了给规则分类&#xff0c;将不同类别的规则写入不同的文件。 在规则加载时两条不同的规则却被认为是重复的&#xff0c;因此记录一下去重逻辑。…...

接口在函数参数和对象类型中的应用

在 TypeScript 中&#xff0c;接口&#xff08;interface&#xff09;是一种强大的工具&#xff0c;用于定义和约束对象的结构。它不仅可以用于描述对象类型&#xff0c;还能够用于定义函数的参数和返回类型。接口可以提高代码的可读性、可维护性&#xff0c;并帮助捕捉潜在的错…...

Javascript:数组和函数

数组 创建数组 使用new创建 let arrnew array(数组大小); 直接赋值创建 let Arr2[];let Arr3[1,A,"HELLLO"]; 这里JS的数组里面的元素属性可以各不相同 演示代码 <script>let Arr1new Array(5);let Arr2[];let Arr3[1,A,"HELLLO"];console.…...

Vue Router

Vue Router:前端路由跳转的魔法 什么是Vue Router Vue Router是Vue官方的路由管理器,它允许我们在不重新加载页面的情况下更改浏览器中显示的内容,实现单页应用(SPA)的无缝导航体验。 📊 Vue Router工作流程图 #mermaid-svg-xNtkA0qYMjB0lvUt {font-family:"trebu…...

谷歌与微软的AI战争:搜索、云服务与生态布局

谷歌与微软的AI战争&#xff1a;搜索、云服务与生态布局 系统化学习人工智能网站&#xff08;收藏&#xff09;&#xff1a;https://www.captainbed.cn/flu 文章目录 谷歌与微软的AI战争&#xff1a;搜索、云服务与生态布局摘要引言技术路线对比1. AI基础设施&#xff1a;算力…...

robomaster机甲大师--电调电机

文章目录 C620电调ID设置速率 电调发送报文电调接收报文cubemx程序初始化发送接收 C620电调 ID设置 速率 1Mbps 电调发送报文 发送的数据为控制电机的输出电流&#xff0c;需要将can数据帧的ID设置为0x200 电调接收报文 机械角度&#xff1a;电机的0到360度映射到0到几千转…...

菜鸟之路day31一一MySQL之多表设计

菜鸟之路day31一一MySQL之多表设计 作者&#xff1a;blue 时间&#xff1a;2025.5.9 文章目录 菜鸟之路day31一一MySQL之多表设计0.概述一.多表设计1.1一对多1.2一对一1.3多对多 0.概述 内容学习自黑马程序员BV1m84y1w7Tb 一.多表设计 1.1一对多 一对多关系实现&#xff…...

害怕和别人发生冲突怎么办? --deepseek

害怕与他人发生冲突是一种常见的心理状态&#xff0c;可能源于对关系破裂、被否定或情绪失控的担忧。但这种恐惧长期存在会影响自我表达和人际关系。以下是分步骤的应对策略&#xff0c;帮助你逐步建立应对冲突的自信&#xff1a; 第一步&#xff1a;理解你的恐惧根源 自我提问…...

Go语言——kratos微服务框架使用

文章目录 一、安装依赖二、创建项目三、初始化项目四、使用git_bash命令终端运行命令五、创建自己的项目1、修改app.proto3、internal/service/app.go4、修改internal/service/service.go文件5、创建internal/biz/content.go文件6、修改internal/biz/biz.go文件7、创建internal…...

无人机飞控算法开发实战:从零到一构建企业级飞控系统

简介 无人机飞控算法是实现稳定飞行和精确控制的核心技术,涉及飞行动力学建模、传感器数据处理、状态估计和控制策略等多个环节。本实战指南将系统讲解四旋翼无人机飞控算法的开发流程,包括飞行动力学模型建立、传感器校准与数据融合、主流控制算法实现(PID、ADRC、EKF)以…...

MiniMind:3块钱成本 + 2小时!训练自己的0.02B的大模型。minimind源码解读、MOE架构

大家好&#xff0c;我是此林。 目录 1. 前言 2. minimind模型源码解读 1. MiniMind Config部分 1.1. 基础参数 1.2. MOE配置 2. MiniMind Model 部分 2.1. MiniMindForCausalLM: 用于语言建模任务 2.2. 主干模型 MiniMindModel 2.3. MiniMindBlock: 模型的基本构建块…...

每日算法刷题 Day3 5.11:leetcode数组2道题,用时1h(有点慢)

5.LC 零矩阵(中等) 面试题 01.08. 零矩阵 - 力扣&#xff08;LeetCode&#xff09; 思想: 法一: 利用两个集合分别储存要清0的行和列索引 另外两种原地优化空间的做法暂时不是目前刷题目标&#xff0c;故不考虑 代码 c: class Solution { public:void setZeroes(vector&l…...

POSIX信号量

目录 一、相关概念回顾 1.信号量 2.多线程使用资源的两种情况 3.P操作和V操作 二、CP && 基于环形队列的生产者消费者模型 1.环形队列的介绍 ​编辑 2.基于环形队列的生产者消费者模型的默认规则&#xff08;通过信号量实现规则的成立&#xff09; 3.相关的结论…...

前端Web开发HTML5+CSS3+移动web(基础-flex)

网页设计套路&#xff1a;从上到下&#xff0c;从整体到局部 &#xff11;&#xff1a;HTML定义&#xff1a; &#xff08;1&#xff09;超文本是点击可以页面来回切换的链接 &#xff08;2&#xff09;标记就是标签语言 &#xff12;&#xff1a;标签的语法 &#xff08;1&…...

Java 原生异步编程与Spring 异步编程 详解

简介 Java 异步编程是现代高性能应用开发的核心技术之一&#xff0c;它允许程序在执行耗时操作&#xff08;如网络请求、文件 IO&#xff09;时不必阻塞主线程&#xff0c;从而提高系统吞吐量和响应性。 异步 vs 同步 同步&#xff1a;任务按顺序执行&#xff0c;后续任务需…...

AUTOSAR图解==>AUTOSAR_TR_HWTestManagementIntegrationGuide

AUTOSAR硬件测试管理集成指南 启动和关闭阶段硬件测试管理的规范与集成 目录 文档概述 1.1 文档范围 1.2 局限性目标与动机 2.1 目标 2.2 动机 2.3 用例约束与假设缩略语与术语相关文档HTMSS AUTOSAR集成方法HTMSS功能描述AUTOSAR架构解决方案 8.1 HTMSS系统架构 8.2 HTMSS启动…...

Day22 Kaggle泰坦尼克号训练实战

​ 作业 自行学习参考如何使用kaggle平台&#xff0c;写下使用注意点&#xff0c;并对下述比赛提交代码 kaggle泰坦里克号人员生还预测 一、流程 思路概述 数据加载 &#xff1a;读取泰坦尼克号的训练集和测试集。数据预处理 &#xff1a;处理缺失值、对分类变量进行编码、…...

基于大核感知与非膨胀卷积的SPPF改进—融合UniRepLK的YOLOv8目标检测创新架构

在当前目标检测领域中&#xff0c;YOLO系列模型因其优异的速度-精度平衡能力而被广泛部署于工业界与科研场景。YOLOv8作为该系列的最新版本&#xff0c;在主干网络与特征金字塔结构上进行了多项优化&#xff0c;进一步提升了其实时性与鲁棒性。然而&#xff0c;其核心组件—SPP…...

[Linux]从零开始的STM32MP157 Busybox根文件系统构建

一、前言 在上一篇教程中&#xff0c;已经教了大家如何使用Buildroot构建根文件系统&#xff0c;并且在最后我们已经完整的构建了一个可以运行的根文件系统。但是&#xff0c;Buildroot的集成度太高了&#xff0c;不利于小白理解根文件系统&#xff0c;所以本次教程&#xff0c…...

C++ RAII机制

RAII&#xff08;Resource Acquisition Is Initialization&#xff09;是一种编程范式&#xff0c;核心思想是&#xff1a;资源的生命周期与对象绑定——对象创建时获取资源&#xff0c;对象销毁时自动释放资源。这种机制通过构造函数和析构函数的配对执行&#xff0c;确保资源…...

spring中的@Value注解详解

一、核心功能与作用 Value是Spring框架中用于动态注入属性值的注解&#xff0c;支持从配置文件、环境变量、SpEL表达式等来源注入数据&#xff0c;实现代码与配置的解耦。 注入类型覆盖广泛 基本类型&#xff1a;字符串、数值&#xff08;int/double&#xff09;、布尔值等。 …...

模型欠拟合是什么?

模型的欠拟合:全面解析 一、定义与核心概念 欠拟合(Underfitting)是指模型在训练数据、验证数据和测试数据上均表现不佳的现象。其本质是模型过于简单或学习能力不足,无法捕捉数据中的潜在规律和复杂关系,导致泛化能力差。例如,用线性模型拟合非线性数据时,模型无法描…...

IC ATE集成电路测试学习——电流测试的原理和方法

电流测试 我们可以通过电流来判断芯片的工作状态时&#xff0c;首先先了解下芯片的电流是如何产生的。 静态电流 理论上&#xff0c;CMOS结构的芯片静态时几乎不耗电 CMOS基本结构&#xff1a;Pmos Nmos 串联当逻辑电平稳定时&#xff1a; ➜ 要么Pmos导通&#xff0c;Nmo…...

Wordpress头像无法加载太慢问题解决方式

Wordpress头像无法加载太慢问题解决方式 1、找到我们当前使用的主题目录中找到functions.php文件在文件最后面添加以下代码 if ( ! function_exists( get_cravatar_url ) ) {/***替换Gravatar头像为Cravatar头像** param string $url** return string*/function get_cravatar…...

《大模型微调实战:Llama 3.0全参数优化指南》

全参数微调&#xff08;Full Parameter Fine-Tuning&#xff09;是推动大模型适应垂直领域任务的核心技术&#xff0c;尤其对于Llama 3.0这类千亿级参数模型而言&#xff0c;其性能优化与场景适配能力直接决定了实际应用价值。然而&#xff0c;全参数微调面临计算成本高、内存占…...

ActiveMQ 生产环境问题排查与调优指南(二)

五、调优策略与实践 5.1 JVM 调优 JVM 调优对于提升 ActiveMQ 性能至关重要&#xff0c;合理的 JVM 配置可以使 ActiveMQ 更高效地利用系统资源&#xff0c;减少性能瓶颈。 设置合理的堆内存大小是 JVM 调优的关键步骤。堆内存是 JVM 中用于存储对象实例的区域&#xff0c;其…...

AugmentCode 非常昂贵的新定价

AugmentCode 现在的价格比 Cursor 和 Windsurf 的总和还要贵。 AugmentCode 曾是我开发工作流程的常用工具。出乎意料的是,他们改变了定价结构,让开发者们震惊不已。 原来的30 美元月费已经增长为50 美元月费,这是一个67%的增长。 改变我看法的不仅仅是价格上涨,还有他…...

Unity 红点系统

首先明确一个&#xff0c;即红点系统的数据结构是一颗树&#xff0c;并且红点的数据结构的初始化需要放在游戏的初始化中&#xff0c;之后再是对应的红点UI侧的注册&#xff0c;对应的红点UI在销毁时需要注销对红点UI的显示回调注册&#xff0c;但是不销毁数据侧的红点注册 - …...