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

STL—容器—string类【对其结构和使用的了解】【对oj相关练习的训练】

STL—容器—string类

其实string类准确来说并不是容器,因为他出现的时间比STL要早,但是也可以说是容器吧。

1.为什么要学习string类?

1.1C语言当中的字符串

C语言中,字符串是以’\0’结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。

1.2面试题

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

在OJ中,有关字符串的题目基本以string类的形式出现,而且在常规工作中,为了简单、方便、快捷,基本

都使用string类,很少有人去使用C库中的字符串操作函数。

2.标准库中的string类

2.1string类的了解

string类的文档介绍

  1. 字符串是表示字符序列的类

  2. 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作单字节字符字符串的设计特性。

  3. string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型(关于模板的更多信息,请参阅basic_string)。

  4. string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_traits和allocator作为basic_string的默认参数(根于更多的模板信息请参考basic_string)。

  5. 注意,这个类独立于所使用的编码来处理字节:如果用来处理多字节或变长字符(如UTF-8)的序列,这个类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。

总结:

  1. string是表示字符串的字符串类

  2. 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。

  3. string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator> string;

  4. 不能操作多字节或者变长字符的序列。

使用string类时,必须包含#include头文件以及using namespace std;

我们在包含头文件的时候经常会遇到两个比较容易混淆的头文件引用#include<string.h> 和 #include ,两者的主要区别如下:

#include<string.h>是C语言的标准库,主要是对字符串进行操作的库函数,是基于char*进行操作的,例如常见的字符串操作函数stpcpy、strcat都是在该头文件里面声明的。

#include是C++语言的标准库,该库里面定义了string类,你可以包含这个头文件,然后定义一个字符串对象,对于字符串的操作就基于该对象进行,例如:string str;

一般来说,c++的头文件不会有.h的出现,比如

————————————————

文档方面,个人觉得这个比较好用

- C++ Reference

作为一个程序员,查文档是一个必须具备的基本素质,当遇到一个不熟悉的接口需要使用的时候,查文档就是最好的解决方法,因此我们需要学会怎么查文档。

2.2string类的常用接口说明

2.2.1.string类的常见构造

image-20240617000544460

c++98版本中全部的构造

image-20240617002205885

实例:

// 初识string类 
// string类 有很多的接口和成员函数,我们主要得熟悉的使用其中的2成,剩下的8成需要我们去通过网络查询来解决
# include<string>
# include<iostream>using namespace std;
int main()
{// 关于string类的构造string s1; // 构造string s2("string"); // 直接构造string s3(s2); // 拷贝构造// 上面这三个最常用string s4(10, 's'); // string类中的的不常用的特殊构造string s5("hello", 1, 3); // string类中的不常用的特殊构造 [这里如果给的不是3 是过大的值,那么就是npos,意思就是将字符串后面有多少给多少] 这些知识都可以查文档查到// 能使用 << 来输出是因为 库中 已经对<< 进行了重载cout << s1 << endl; // [这里默认是空串]cout << s2 << endl; // stringcout << s3 << endl; // stringcout << s4 << endl; // sssssssssscout << s5 << endl; // ell [取"hello"中的 第 1~3号元素来构造的, 正好就是ell]string s6 = s4; // 这里还是拷贝构造string s7 = "string"; // 这里正常来说就是发生隐式类型转化 意味着,先直接构造,在拷贝构造// 但是这里的直接构造 + 拷贝构造发生在了同一表达式,因此编译器直接优化成直接构造cout << s6 << endl; // sssssssssscout << s7 << endl; // stringreturn 0;
}
2.2.2string类对象的容量操作

常见的与容量相关的接口:

image-20240617005128425

接口的代码使用:

1.size(重点)

size可以统计字符串的有效字符长度并返回。一般来说会用作遍历。

// string类对象有关容量的常见接口
// 1.size()
# include<string>
# include<iostream>
using namespace std;int main()
{	string s("hello");cout << "s: ";// 遍历for (size_t i = 0; i < s.size(); i++){s[i] += 1; // 写 [这个相当于对取出来的字符+1,就相当于给其ASCII码值+1]}for (size_t i = 0; i < s.size(); i++){cout << s[i]; // 读}cout << endl; //s8: ifmmp [hello每个字符的ASCII码值分别+1]return 0;
}
2.length

length也会统计字符串的有效长度并返回。其实和size是非常像的,甚至其底层的实现原理都是一样的、

其实在早期的时候用的都是length,但是后面数据结构都用size了,就统一用size了,length和size可以说就是一样的,这里推荐使用size

// 2.length# include<string>
# include<iostream>
using namespace std;void test_string()
{string s1("hello, world");cout << s1.size() << endl; // 12cout << s1.length() << endl; // 12}int main()
{test_string();return 0;
}
3.capacity

capacity可以返回当前空间的总大小

// 3.capacity
# include<string>
# include<iostream>
using namespace std;int main()
{string s("hello, world");cout << s.capacity() << endl; // 15// 目前s有12个空间存放数据,如果超出空间,会增容多少呢?s += "ddddd";cout << s.capacity() << endl; // 31// 可以发现,直接将其增容到了2倍的容量。return 0;
}
4.clear(重点)

清空字符串内容 【注意,空间不会被销毁,只是内容被清空了】

// 4.clear# include<string>
# include<iostream>
using namespace std;int main()
{string s("hello, world");cout << s << endl; // hello, worlds.clear();cout << s << endl; // 空串// 我们清除了字符串内容,不代表空间被销毁了、cout << s.capacity() << endl; // 15return 0;
}
5.empyt(重点)

empty可以检测字符串是否为空串,是就返回true,不是就返回false

//5.empty# include<string>
# include<iostream>
using namespace std;int main()
{string s("hello, world");if (s.empty())cout << "该string类对象为空" << endl;elsecout << "该string类对象不为空" << endl;s.clear();if (s.empty())cout << "该string类对象为空" << endl;elsecout << "该string类对象不为空" << endl;return 0;
}
6.reserve/resize(重点)

在学习这两个接口之前,我们先来看一个代码:

这个代码可以观察到每次增容时容量的变化大小

# include<string>
# include<iostream>
using namespace std;int main()
{string s;size_t sz = s.capacity();cout << "s_capacity grow\n";for (int i = 0; i < 100; i++){s.push_back('c'); // 也可以s += 'c'if (sz != s.capacity()) // != 的时候就说明s进行了增容{sz = s.capacity(); // 将新容量给到szcout << "new capacity: " << sz << endl; // 将此时的容量打印出来}}return 0;
}

image-20240617164339500

我们发现是接近1.5倍增容的。但是我们之前知道15容量在增容的时候是按两倍增容增到31的、

规律是:空间基数越大,增容的倍数越小

因为增容是有代价的。增容越多的空间,执行越多次增容,代价就越大。

知道了这个小知识,我们就可以来看resize和reserve了。

reserve给字符串预留空间。

其实就是给字符串开辟空间

// 6.resize/reserve
# include<string>
# include<iostream>
using namespace std;int main()
{string s;s.reserve(100); // 我们知道s需要100个空间,提前开好空间size_t sz = s.capacity();cout << "s_capacity grow\n";for (int i = 0; i < 100; i++){s.push_back('c'); // 也可以s += 'c'if (sz != s.capacity()) // != 的时候就说明s进行了增容{sz = s.capacity(); // 将新容量给到szcout << "new capacity: " << sz << endl; // 将此时的容量打印出来}}return 0;
}

image-20240617165039849

由于我们提前开辟好了100个空间【实际上不一定会开100个,因为要考虑内存对齐,最后还要放个\0】,s没有再执行增容操作。

这样我们只增容了一次,比起自动增容的次数就减少了许多。

image-20240617165453007

直接开辟了111个空间,并不是我们输入的100。【了解一下】

resize:在reserve的基础上,将有效字符的个数修改,并填充字符

int main()
{string s;//s.reserve(100); // 我们知道s需要100个空间,提前开好空间s.resize(100); // 将有效字符个数修改到100,并填充\0size_t sz = s.capacity();cout << "s_capacity grow\n";for (int i = 0; i < 100; i++){s.push_back('c'); // 也可以s += 'c'if (sz != s.capacity()) // != 的时候就说明s进行了增容{sz = s.capacity(); // 将新容量给到szcout << "new capacity: " << sz << endl; // 将此时的容量打印出来}}return 0;
}

image-20240617165704803

可以看到它相当于reserve(100)并且将有效字符个数修改到了100,并且填充了\0、

image-20240617171034866

由于前100个空间被resize填充了\0,因此后面还是会执行增容操作

因此如果想后面不执行增容操作,这里要用reserve

我们还可以指定填充的字符

int main()
{string s;//s.reserve(100); // 我们知道s需要100个空间,提前开好空间//s.resize(100); // 将有效字符个数修改到100,并填充\0s.resize(100,'6'); // 将有效字符修改到100,并填充6// 因此如果想后面不执行增容操作,这里要用reservesize_t sz = s.capacity();cout << "s_capacity grow\n";for (int i = 0; i < 100; i++){s.push_back('c'); // 也可以s += 'c'if (sz != s.capacity()) // != 的时候就说明s进行了增容{sz = s.capacity(); // 将新容量给到szcout << "new capacity: " << sz << endl; // 将此时的容量打印出来}}return 0;
}

image-20240617165942993

可以看到不再填充\0而是填充我们指定的字符6

image-20240617170946266

并且由于前100个空间都被resize填入了元素,因此后面插入100个c的时候还是会增容的

因此如果想后面不执行增容操作,这里要用reserve

总结(重要):

string容量相关方法使用代码演示

  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在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变
string s1("hello, world");
s1.resize(5); // 会将原来的size减小到 5。
cout << s1 << endl; // hellos1.resize(20, 'x'); // 将size改到20,由于前5个已经有元素了,后15个填入x【空间不够会自动扩容】
cout << s1 << endl; // helloxxxxxxxxxxxxxxx
  1. reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于string的底层空间总大小时,reserver不会改变容量大小
注意:
  1. 上面我们讲的有关容量的接口都是常用的,并不是全部。全部的接口可以去文档中查看
  2. 在查看接口的时候,目前来说只需要查看接口如何使用,不用查看接口的底层实现,因为以目前的水平,是完全看不懂的。
  3. 常用的接口需要熟悉使用、

2.2.3string类对象的访问及遍历操作

image-20240617183351615

string中元素访问及遍历代码演示

下面我们对于遍历一共有3种方法【[] + 下标】【迭代器】【范围for】

拓展:迭代器

我们来了解一下迭代器

image-20240617120225600

如图所示,迭代器一共四种,这里我们就是了解一下,具体的我们后面会详细学习。

遍历的代码:

void test_string3()
{// 迭代器一共有4种迭代器,这里在介绍一种string s("hello world");// 倒着遍历string::reverse_iterator rit = s.rbegin(); //要用rbegin,指向字符串的最后一个有效元素// auto rit = s.rbegin();while (rit != s.rend()) // rend,指向字符串的首元素{cout << *rit;rit++; // 其实是倒着走的}cout << endl;// dlrow olleh
}int main()
{	// [] + 下标  【平常推荐使用这个】string s("hello");cout << "s: ";// 遍历for (size_t i = 0; i < s.size(); i++){s[i] += 1; // 写 [这个相当于对取出来的字符+1,就相当于给其ASCII码值+1]}for (size_t i = 0; i < s.size(); i++){cout << s[i]; // 读}cout << endl; //s8: ifmmp [hello每个字符的ASCII码值分别+1]// 迭代器【每个容器都会有自己的迭代器】【迭代器一共有四种】(这里了解一下迭代器)string::iterator it = s.begin(); // string::iterator 实际是一个类型//auto it = s1.begin(); // 也可以这样写 [自动推演]// 遍历while (it != s.end()){*it -= 1; // 写++it; // 迭代}it = s.begin(); // 重置while (it != s.end()){cout << *it; // 读++it; // 迭代}cout << endl;// hellotest_string3(); // 另外一种迭代器的使用// 上述是迭代器的使用方法,熟悉一下就行,后面会详细学习。// 范围for(c++11)  [原理其实就是迭代器]for (auto ch : s) // 自动把s8从左到右依次给到ch auto会自动推断类型{cout << ch;}cout << endl; //helloreturn 0;
}

再来看一个场景:

  • 将字符串转化为整数
// 将传进来的字符串转化成整数 【使用迭代器】
int string2int(const string& str)// 用引用提高效率,并且不改变str,const修饰
{int val = 0;string::const_iterator cit = str.begin(); // 此时的str是const对象,返回的是const类型的迭代器类型while (cit != str.end()){val *= 10;val += (*cit - '0');}cout << val << endl;
}int main()
{// 将字符串转化成整数string s("12345");int val = 0; for (int i = 0; i < s.size(); i++) // 遍历{// 让val + 上取出来的数字,然后再*10, 循环,直至全部取出、val *= 10; val += (s[i] - '0'); // -'0'是因为 取出来的是字符 而不是整形, - 去'0'的ASCII值才是对应整形}cout << val << endl; // 12345string s1("12345");int ret = string2int(s1);cout << ret << endl; // 12345return 0;
}

通过上面两个例子,我们了解到了遍历有不同的方式去遍历。

在这里面当中,迭代器似乎是一个比较麻烦的使用方式。

从方向上分:正向和反向迭代器

从权限上分:普通迭代器,const迭代器。

因此使用的时候似乎不吃香,从短期来看确实是如此,因为经过了c语言的学习,我们已经习惯于用数组的方式去遍历。但是数组这个方式只能用于vector 和 string。等到后面更多的数据结构,也就是容器出现,迭代器就会频繁使用,因为+所有容器的迭代器使用方法都是类似的。

因此迭代器的学习是c++中一个重要的部分。迭代器可以用于所有STL库中的容器的遍历

2.2.4string类对象的修改操作

常见的几个与修改操作相关的接口

image-20240617194843211

1.push_back/append/+=(重要)

push_back:在字符串后边尾插字符

append:在字符串后边追加字符串

+=:在字符串后边追加字符串

这三个我们推荐**+=**

有关尾插的三个接口的使用的代码:

// 1.+= / push_back / append 【有关尾插的三个接口】
# include<string>
# include<iostream>
using namespace std;int main()
{string s1;s1.push_back('p');s1 += 'p'; // 推荐使用+=cout << s1 << endl; //pp  可以看到 += 和 调用尾插接口的结果是一样的s1 += "hello"; // 这里调用的是另外一个 += 的重载函数cout << s1 << endl; // pphellos1.append(" world"); // 在s1后尾插" world"字符串cout << s1 << endl; // pphello world// 在上述三个尾插的操作接口中,我们推荐使用+=return 0;
}

如果想在具体位置插入的话可以使用insert接口,想删除具体位置的话用erase接口

可以上文档查具体使用方法,这里不做详解

2.c_str

image-20240618130116327

c_str: 会将string类对象以const char 的格式返回*

他会在string类对象中找到\0 ,把\0之前的字符串内容当做一个 const char 类型返回*

代码操作:

//2.c_str
# include<string>
# include<iostream>
using namespace std;int main()
{string s("hello");const char* str = s.c_str();// str拿到的是const char* 类型。// 获取字符数组的首地址,用c语言中的字符串形式去遍历while (*str) // 只要*str不是\0就继续{cout << *str;str++;}cout << endl; // hello// 也可以直接输出cout << s << endl; // hello// 这里调用的是string类中的重载的<<, 它会将整个s进行输出cout << s.c_str() << endl; // hello// 这里相当于直接对 const char* 进行输出,遇到\0就会结束输出s += '\0';s += ' ';s += "world";cout << s << endl; // hello world  中间的\0是不可见字符,不会打印出来cout << s.c_str() << endl; // hello  因为从h到 \0 被当做了一个const char*,s.c_str()返回的就是 helloreturn 0;
}
拓展:编码表

我们知道一个中文占据两个字节,这是由编码表实现的,因为中文的编码表是GDK

而英文的编码表是ASCII表

编码的本质是:映射,将对应的值映射到表中的某一个位置。

我们来看一段代码:

// 编码表
# include<string>
# include<iostream>
using namespace std;int main()
{// 我们知道英文是用ASCII码表来编码的string s = "中国"; // 中文使用GDK来编码的cout << s << endl; //中国s[3] = -7;cout << s << endl; // 中郭s[3] = -8;cout << s << endl; // 中锅s[3] = -9;cout << s << endl; // 中棍return 0;
}

下图是s字符串中存储的信息:

image-20240618234637031

可以很清楚的看到中文是两个字节共同编码成一个中文。

3.find/rfind/substr(重要)

image-20240619005428685

find: 可以查询字符串中的某个字符

如果找不到会返回npos【rfind找不到也一样返回npos】

image-20240620170550769

npos是-1 size_t的-1相当于int类型的最大值,42亿多

image-20240620171505713

substr:可以返回指定位置以后得字符串。如果不指定就是默认从头开始

来看一段代码:

//3.find/rfind
# include<string>
# include<iostream>
using namespace std;int main()
{string s("text.cpp"); // 如果我们想查询这个后缀.cpp// 我们就可以用find去查询.  再将其后边的打印出来size_t pos = s.find('.'); // pos = 4if (pos != string::npos) // npos是-1  size_t的-1相当于int类型的最大值,42亿多{// 用substr接口实现.后的打印cout << s.substr(pos) << endl; // .cpp// s.substr(pos)就是打印pos之后的数据,包括pos}return 0;
}

rfind:可以查询字符串中的一个字符,并且是最后一次出现的

//3.find/rfind
# include<string>
# include<iostream>
using namespace std;int main()
{string s("text.cpp"); // 如果我们想查询这个后缀.cpp// 我们就可以用find去查询.  再将其后边的打印出来size_t pos = s.find('.'); // pos = 4if (pos != string::npos) // npos是一个-1  size_t的-1相当于int类型的最大值,42亿多{// 用substr接口实现.后的打印cout << s.substr(pos) << endl; // .cpp// s.substr(pos)就是打印pos之后的数据,包括pos}// 还会有一种情况,如果想查询的字符串有多个后缀,我们想输出最后一个后缀的话// 我们就可以用rfind 重复的查找.  找到最后一个.string s1("test.cpp.zip"); // pos1 = 8size_t pos1 = s1.rfind('.');if (pos1 != string::npos){cout << s1.substr(pos1) << endl; // .zip}return 0;
}
拓展:网址

平常我们看到的网址是由协议、域名、资源名称

// 网址
# include<string>
# include<iostream>
using namespace std;// 协议、域名、资源名称
int main()
{string url("https://gitee.com/wzf-sang");//https是协议 http和https的差别就是https是加密的///gitee.com其实是个ip但是绑定了域名。如果不绑定域名这里是个ip【域名更好记住】//wzf-sang就是资源名称。//【在我们输入网址敲回车之后,服务器做的第一件事就是将你输入的网址拆成协议、域名、资源名称】//【因此才能在对应协议下申请访问对应的域名并根据资源名称返回资源】// 现在有个要求:分离出url。size_t i1 = url.find(':'); // i1 = 5if (i1 != string::npos){cout << url.substr(0, i1) << endl; // https//从0这个下标,输出i1个元素}size_t i2 = url.find('/', i1 + 3); // 从i1 + 3开始找if (i2 != string::npos){cout << url.substr(i1 + 3, i2 - i1 - 3) << endl;// gitee.com// 从i1 + 3这个下标,输出i2 - i1 - 3个元素}cout << url.substr(i2 + 1) << endl; //wzf-sangreturn 0;
}

而我们可以将其功能分离出来,抽象成一个函数

// 网址
# include<string>
# include<iostream>
using namespace std;// 该函数用于分离网址的三个必要信息
void spilt_url(const string& url)
{//【在我们输入网址敲回车之后,服务器做的第一件事就是将你输入的网址拆成协议、域名、资源名称】//【因此才能在对应协议下申请访问对应的域名并根据资源名称返回资源】// 现在有个要求:分离出url。size_t i1 = url.find(':'); // i1 = 5if (i1 != string::npos){cout << url.substr(0, i1) << endl; // https//从0这个下标,输出i1个元素}size_t i2 = url.find('/', i1 + 3); // 从i1 + 3开始找if (i2 != string::npos){cout << url.substr(i1 + 3, i2 - i1 - 3) << endl;// gitee.com// 从i1 + 3这个下标,输出i2 - i1 - 3个元素}// 在i2这个下标后面的都是资源名称cout << url.substr(i2 + 1) << endl; // wzf-sang// + 1是因为i2是 / ,i2后面的才是资源名称
}// 协议、域名、资源名称
int main()
{string url("https://gitee.com/wzf-sang");string url2("https://leetcode.cn/problems/majority-element/description/?envType=study-plan-v2&envId=top-interview-150");//https是协议 http和https的差别就是https是加密的///gitee.com其实是个ip但是绑定了域名。如果不绑定域名这里是个ip【域名更好记住】//wzf-sang就是资源名称。spilt_url(url);//	https//	gitee.com//	wzf - sangspilt_url(url2);//https//leetcode.cn//problems/majority-element/description/?envType=study-plan-v2&envId=top-interview-150return 0;
}

2.2.5string类非成员函数

image-20240619011949494

上面的几个接口了解一下,下面的OJ题目中会有一些体现他们的使用。string类中还有一些其他的操作,这里不一一列举,在需要用到时不明白了查文档即可。

2.2.6.vs和g++下string结构的说明

注意:下述结构是在32位平台下进行验证,32位平台下指针占4个字节。

  • vs下string的结构

string总共占28个字节,内部结构稍微复杂一点,先是有一个联合体,联合体用来定义string中字

符串的存储空间

当字符串长度小于16时,使用内部固定的字符数组来存放

当字符串长度大于等于16时,从堆上开辟空间

这种设计是合理的,大多数情况下字符串的长度都小于16,那string对象创建好之后,内

部已经有了16个字符数组的固定空间,不需要通过堆创建,效率高。

其次:还有一个size_t字段保存字符串长度,一个size_t字段保存从堆上开辟空间总的容量

最后:还有一个指针做一些其他事情。

故总共占16+4+4+4=28个字节

image-20240705222701207

  • g++下string的结构

G++下,string是通过写时拷贝实现的,string对象总共占4个字节,内部只包含了一个指针,该指

针将来指向一块堆空间内部包含了如下字段:

  1. 空间总大小

  2. 字符串有效长度

  3. 引用计数

struct _Rep_base
{size_type _M_length;size_type _M_capacity;_Atomic_word _M_refcount;
};
  1. 指向堆空间的指针,用来存储字符串。

2.2.7与string相关的oj题目

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

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

  3. 字符串最后一个单词的长度

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

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

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

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

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

  9. 找出字符串中第一个只出现一次的字符较难

相关文章:

STL—容器—string类【对其结构和使用的了解】【对oj相关练习的训练】

STL—容器—string类 其实string类准确来说并不是容器&#xff0c;因为他出现的时间比STL要早&#xff0c;但是也可以说是容器吧。 1.为什么要学习string类&#xff1f; 1.1C语言当中的字符串 C语言中&#xff0c;字符串是以’\0’结尾的一些字符的集合&#xff0c;为了操作…...

讲个SystemVerilog随机约束小坑

正文 记录个在写SystemVerilog随机约束时遇到的一个小坑&#xff0c;如果没有认真去查看随机结果是否符合预期&#xff0c;还真不容易发现。 为了方便讲述&#xff0c;写了如下示例代码。类cl_a里有个随机变量aa&#xff0c;初始值为222。在module top里对类cl_a例化并进行约…...

mysql在windows下的安装

软件安装 配置环境变量 测试...

uniapp 在手机上导出excel

1.创建excelDev.js文件 export default {exportExcel(fileData, documentName excel) {plus.io.requestFileSystem(plus.io.PUBLIC_DOCUMENTS, function(fs) {let rootObj fs.rootlet fullPath rootObj.fullPathconsole.log("开始导出数据")// 创建文件夹rootObj…...

收银系统源码-收银台副屏广告

1. 功能描述 门店广告&#xff1a;双屏收银机&#xff0c;副屏广告&#xff0c;主屏和副屏同步&#xff0c;总部可统一控制广告位&#xff0c;也可以给门店开放权限&#xff0c;门店独立上传广告位&#xff1b; 2.适用场景 新店开业、门店周年庆、节假日门店活动宣传&#x…...

【TORCH】torch.normal()中的size参数

在 torch.normal() 函数中&#xff0c;size 参数用于指定生成张量的形状。torch.normal() 函数用于从正态&#xff08;高斯&#xff09;分布中生成随机数。函数的基本形式是&#xff1a; torch.normal(mean, std, size)mean&#xff1a;均值&#xff0c;可以是标量或张量。如果…...

【第20章】MyBatis-Plus逻辑删除支持

文章目录 前言一、逻辑删除的工作原理二、支持的数据类型三、使用方法1.配置全局逻辑删除属性2.在实体类中使用 TableLogic 注解 四、常见问题解答1. 如何处理插入操作&#xff1f;2. 删除接口自动填充功能失效怎么办&#xff1f; 五、实战1. 全局配置2. 添加TableLogic3. 自动…...

【IT领域新生必看】 Java编程中的重载(Overloading):初学者轻松掌握的全方位指南

文章目录 引言什么是方法重载&#xff08;Overloading&#xff09;&#xff1f;方法重载的基本示例 方法重载的规则1. 参数列表必须不同示例&#xff1a; 2. 返回类型可以相同也可以不同示例&#xff1a; 3. 访问修饰符可以相同也可以不同示例&#xff1a; 4. 可以抛出不同的异…...

python转文本为语音并播放

python转文本为语音并播放 1、导入库 pip install pyttsx3==2.902、流程 1、初始化tts引擎 2、设置音量(0到1之间) 3、设置语速 4、 设置声音对象,voices[0].id代表男生,voices[1].id代表女生 5、转换文本并播放 6、挂起声音引擎3、代码 # -*- coding: utf-8 -*-"…...

解锁高效软件测试:虚拟机助力提升测试流程的秘诀

众所周知&#xff0c;软件测试在软件开发生命周期中至关重要。它确保软件符合要求&#xff0c;没有漏洞&#xff0c;并帮助开发人员优化性能&#xff0c;验证项目功能。 然而&#xff0c;测试可能既耗时又耗费资源&#xff0c;特别是当需要在不同操作系统和配置上测试软件组件…...

创建vue3项目

npm create vuelatest 编译打包生成报告 yarn add rollup-plugin-visualizer vite.config.ts: import { fileURLToPath, URL } from node:urlimport { defineConfig } from vite import vue from vitejs/plugin-vue import vueJsx from vitejs/plugin-vue-jsx import vueDevTo…...

中国网络安全审查认证和市场监管大数据中心数据合规官CCRC-DCO

关于CCRC-DCO证书的颁发机构&#xff0c;它是由中国网络安全审查认证与市场监管大数据中心&#xff08;简称CCRC&#xff09;负责。 该中心在2006年得到中央机构编制委员会办公室的批准成立&#xff0c;隶属于国家市场监督管理总局&#xff0c;是其直辖的事业单位。 依据《网络…...

Web漏洞扫描工具AppScan与AWVS测评及使用体验

AppScan和AWVS业界知名的Web漏洞扫描工具&#xff0c;你是否也好奇到底哪一个能力更胜一筹呢&#xff1f;接下来跟随博主一探究竟吧。 1. 方案概览 第一步&#xff1a;安装一个用于评测的Web漏洞靶场&#xff08;本文采用最知名和最广泛使用的靶场&#xff0c;即OWASP Benchma…...

瞰景Smart3D使用体验分享

引言 作为一名建筑设计师&#xff0c;我一直在寻找能够提升工作效率和设计质量的软件工具。瞰景Smart3D&#xff08;Smart3D&#xff09;是一款备受推崇的3D建模和设计软件&#xff0c;广泛应用于建筑、工程和施工&#xff08;AEC&#xff09;行业。经过一段时间的使用&#x…...

Android系统adb shell dumpsys activity processes

在Android系统中&#xff0c;adb shell dumpsys activity processes 命令是一个非常强大的工具&#xff0c;用于获取当前系统中所有运行进程的详细信息&#xff0c;包括它们的状态、内存使用情况、任务栈等。这对于开发者来说非常有用&#xff0c;尤其是在调试应用、分析系统性…...

vue侦听器watch()

侦听器watch&#xff08;&#xff09; 侦听器侦听数据变化&#xff0c;我们可以使用watch 选项在每次响应式属性变化时触发一个函数。 <template><h3>侦听器watch</h3><hr> <p>{{nessage}}</p> <button click"exchage">…...

如何用Python向PPT中批量插入图片

办公自动化办公中&#xff0c;Python最大的优势是可以批量操作&#xff0c;省去了用户粘贴、复制、插入等繁琐的操作。经常做PPT的朋友都知道&#xff0c;把图片插入到PPT当中的固定位置是一个非常繁琐的操作&#xff0c;往往调整图片时耗费大量的时间和精力。如何能省时省力插…...

C# Socket

Socket命名空间&#xff1a;创建 Socket&#xff1a;连接到服务器&#xff08;客户端&#xff09;&#xff1a;绑定和监听&#xff08;服务器端&#xff09;&#xff1a;接受连接&#xff08;服务器端&#xff09;&#xff1a;发送和接收数据&#xff1a;关闭 Socket&#xff1…...

node的下载、安装、配置和使用(node.js下载安装和配置、npm命令汇总、cnpm的使用)

天行健,君子以自强不息;地势坤,君子以厚德载物。 每个人都有惰性,但不断学习是好好生活的根本,共勉! 文章均为学习整理笔记,分享记录为主,如有错误请指正,共同学习进步。 愿将腰下剑,直为斩楼兰。 ——《塞下曲》 文章目录 一、node.js的下载、安装和配置1. node.js下…...

深度卷积神经网络 AlexNet

一、机器学习深度学习的发展 1、机器学习SVM方法 &#xff08;1&#xff09;20世纪90年代&#xff0c;基于统计学习理论的结果&#xff0c;开发了一种新型的学习算法——支持向量机&#xff08;SVM&#xff09;。这就产生了一类新的理论上优雅的学习机器&#xff0c;它们将SVM…...

ubuntu搭建nfs服务centos挂载访问

在Ubuntu上设置NFS服务器 在Ubuntu上&#xff0c;你可以使用apt包管理器来安装NFS服务器。打开终端并运行&#xff1a; sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享&#xff0c;例如/shared&#xff1a; sudo mkdir /shared sud…...

【OSG学习笔记】Day 18: 碰撞检测与物理交互

物理引擎&#xff08;Physics Engine&#xff09; 物理引擎 是一种通过计算机模拟物理规律&#xff08;如力学、碰撞、重力、流体动力学等&#xff09;的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互&#xff0c;广泛应用于 游戏开发、动画制作、虚…...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地

借阿里云中企出海大会的东风&#xff0c;以**「云启出海&#xff0c;智联未来&#xff5c;打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办&#xff0c;现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...

【项目实战】通过多模态+LangGraph实现PPT生成助手

PPT自动生成系统 基于LangGraph的PPT自动生成系统&#xff0c;可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析&#xff1a;自动解析Markdown文档结构PPT模板分析&#xff1a;分析PPT模板的布局和风格智能布局决策&#xff1a;匹配内容与合适的PPT布局自动…...

unix/linux,sudo,其发展历程详细时间线、由来、历史背景

sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...

在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用

1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...

Element Plus 表单(el-form)中关于正整数输入的校验规则

目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入&#xff08;联动&#xff09;2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解

JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用&#xff0c;结合SQLite数据库实现联系人管理功能&#xff0c;并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能&#xff0c;同时可以最小化到系统…...

mac 安装homebrew (nvm 及git)

mac 安装nvm 及git 万恶之源 mac 安装这些东西离不开Xcode。及homebrew 一、先说安装git步骤 通用&#xff1a; 方法一&#xff1a;使用 Homebrew 安装 Git&#xff08;推荐&#xff09; 步骤如下&#xff1a;打开终端&#xff08;Terminal.app&#xff09; 1.安装 Homebrew…...