C++的string类

目录

一、导入

二、接口学习

1.默认成员函数

2.迭代器相关的函数iterator

3.与容量相关的函数Capacity系列

4.与成员权限相关的函数Element access:

5.修改器相关的函数Modifiers:

6.字符串操作接口函数String operations:​编辑

三、扩展


一、导入

学习过C++类的相关知识之后,我们便可以进行类的进阶学习。在C++的标准库中,有一些内置的类,string便是其中之一。它定义在头文件<string>中。这个类提供了丰富的接口来操作字符串,比如字符串的拼接、查找、替换、插入、删除等。

string也是一个模板,被typedef过。最开始是basic_string这样一个类。

值得一提的是,在学习string的过程中可能会有些许“逆风”。但是学习完这个类之后,再学习其他类,往后便是“大顺风”。

二、接口学习

string - C++ Reference (cplusplus.com),在CPP的官网中,提供了大量的关于string接口函数的介绍,本文旨在了解并熟悉使用string类的接口函数。

String class

Strings are objects that represent sequences of characters.    这是官网中对u有string类的介绍,说明了string类是一个管理字符数组的类。

在网站中对于string类的接口函数列举如下。

其中有默认成员函数Member functions,有迭代器相关的函数Iterators,有容量相关的函数Capacity,访问成员的接口函数:Element access,有负责修改的修改器函数Modifiers,还有String operations函数用来进行字符串操作,以及内部的一个成员常量npos和一些全局的函数Non-member function overloads。

可以看出,string的接口是非常繁多的,甚至有一些可以用冗杂形容。下面将以上述列举顺序为序,进行接口的一一列举学习。

1.默认成员函数

文献中列出了三个默认成员函数,constructor构造、destructor析构、operator=赋值重载。

constructor

这是构造函数内部的介绍,总共实现了7中函数重载,其中重要的有默认构造、拷贝构造……多种多样的函数,提供了大量不同的初始化方式。初始化可以传字符、字符串、对象……

(1) (4)是一个带参数、一个不带参数的类型。

这是关于函数的解释。(1)会被初始化为0。

演示:

#include <iostream>
#include <string>

using namespace std;

int main()
{
	string s1;
	string s2("hello world");
	
	cin >> s1;

	cout << s1 << endl;
	cout << s2 << endl;

	return 0;
}

在上述代码中,我们利用string类新建了两个对象,s1调用了默认构造中的缺省函数,s2调用了函数(4)。

由于string类中完成了 流提取和流插入 的重载,因此可以直接使用这两个操作符。当我们屏蔽s1的流提取操作之后,打印是这样的。

取消屏蔽之后,便可以完成打印输入的内容。

当然,内部也包括拷贝构造

int main()
{
	string s2("hello world");
	string s3(s2);


	cout << s3 << endl;
	cout << s2 << endl;

	return 0;
}

这段代码便是利用s2去拷贝构造s3。

我们也可以用字符串的一部分去传参。

对于hello world,假如我们只想传入world,便可以用这个函数。

int main()
{
	string s2("hello world");
	string s3(s2, 6, 5);
	cout << s3;

	return 0;
}

w的下标是6,字符串的长度是5。

对于这个长度5,我们也不需要手动计算。

    string s3(s2, 6, s2.size() - 6);
我们只需要让总的长度 - w的下标即可。size()接口计算的是大小(即长度length,不包含0)。

注意到,len参数给了一个缺省值,nops,这是定义在string中的一个成员。下面是string::npos的解释

给出的值是-1。但是由于是size_t类型,所以这是整数的最大值:42亿。但显然,这会造成越界。因此下面给出的解释中, means "until the end of the string".表示,如果字符串小于npos的长度,就到达字符串的末尾。

当然,我们给函数传入缺省参数时,必须是从右到左缺省!

还可以用n个字符去初始化。

还可以采用迭代器(下面介绍)去初始化。

传入迭代器的一段区间去初始化。当上述初始化时,由于迭代器支持++、--操作,我们去掉了首尾。

当然,最多的:默认构造、拷贝构造、字符串构造

destructor析构函数

这是这是析构函数,用来完成资源的清理。由于析构函数自动调用,所以不需要过多处理。

operator=赋值重载

可以采用字符、字符串、对象传参,去完成赋值运算。其返回值是*this。

实例:

2.迭代器相关的函数iterator

引入

遍历字符串时,我们可以有两种方式,但是学习完迭代器之后,方式便多了一种。

方式一:下标法

由于string重载了[]操作符,我们可以直接用[]去完成资源的访问。

int main()
{
	string s2("hello world");
	for (size_t i = 0; i < s2.size(); i++)
	{
		cout << s2[i] << ' ';
	}
	cout << '\n';

	return 0;
}

方法二:迭代器法

string提供了begin 和 end 两个成员函数,函数简介中说明了,返回的是一个迭代器的beginning和end,用来遍历字符串。begin和end的行为和指针类型类似,但是只能说是用起来类似,却不是完全一致!

这是关于begin和end的函数介绍

值得一提的是,end指向字符串最后一个字符的迭代器,即返回的是/0的位置,而不是最后一个输入的有效字符。因为字符串除了存储我们需要的有效数据,还会增加一个/0。

他们的返回类型都是iterator和const修饰后的iterator两种。因此我们在使用的时候,应该用iterator类型来建立变量。由于迭代器是众多类都有的,因此我们应该声明,这是string的迭代器。

实例:

int main()
{
	string s2("hello world");
	
	string::iterator bg = s2.begin();
	string::iterator end = s2.end();

	while (bg != end)
	{
		cout << *bg << ' ';
		bg++;
	}

	cout << endl;
	return 0;
}

在上述遍历中,我们采用了解引用*和++操作。这两种操作都是指针在遍历时常常用到的操作,所以说迭代器的行为类似指针。

注意,我们写的是bg != end。这种写法主要为了和别的迭代器完成形式的统一。

其实string的迭代器可以写成bg < end;也可以。原因是string的字符串内部的存储空间是连续的。

但是到了vector这种类,内部的空间不连续时,我们只能使用!=,而不是<。

对于不连续的空间,指针减法、指针加法都是未定义的! 在后续的容器中,指针++是允许的,这是因为进行了操作符的重载。否则要进行cur = cur->next;的操作。

这就体现了C++的封装!只需要给你接口,让你用就好!

当然,上述的迭代器由于没有被const修饰,因此可以完成写的操作。

我们将字符串修改成了a。

当然也有被const修饰过的迭代器,来完成只读操作。

这时候,迭代器的返回类型就成了const_iterator类型。

int main()
{
    const string s2("hello world");
	
	string::const_iterator bg = s2.begin();
	string::const_iterator end = s2.end();

	while (bg != end)
		cout << *bg++;

	cout << endl;
	return 0;
}

这就是只读操作。迭代时,先用*bg,再bg++。当然,对于这种复杂的类型,我们可以直接用auto代替。

int main()
{
    const string s2("hello world");

    auto bg = s2.begin();
    auto end = s2.end();

    while (bg != end)
        cout << *bg++;

    cout << endl;
    return 0;
}

方式三:采用范围for

int main()
{
    
    string s2("hello world");

	for (auto& ch : s2)
	{
        ch = 'a';
		cout << ch;
	}
	cout << endl;

	return 0;
}

范围for只需要用 变量 :容器,就可以完成容器中内容的读取。范围for的特点是:自动迭代、自动结束。此处可以用&类型,也可以用auto ch : s2进行迭代,但是无法完成数据的修改!

当然,如果我们观察反汇编,就会观察到其实范围for的本质还是迭代器!

在begin和end的下面,还有两个

rbegin和rend表示的是reverse,表示的是逆置的迭代器。

int main()
{
	string s2("hello world");

	auto bg = s2.rbegin();
	auto end = s2.rend();

	while (bg != end)
		cout << *bg++;

	return 0;
}

通过上述的使用,就实现了逆置的迭代。

因此迭代器有const修饰和非const修饰、正向、逆向四个版本。

其实每个容器都有自己的迭代器,但是这些迭代器的行为都是十分相似!

c修饰的这几个迭代器则是C++11才支持的语法。c表示const,但是我们基本不用,由于原来的begin本身就是有重载过const过的,我们只需要auto去推断类型就可以。

3.与容量相关的函数Capacity系列

Part 1

这里面已经有我们之前用过的size()函数。

我们为什么说string设计的十分冗杂,这里也能体现。

其实size()与length()接口都是计算长度的,只不过为了和其他容器统一(string出现的比较早,出现string时,STL还没有问世呢!),才出现了size()接口。

Part 2

下一个时max_size函数,这个函数用来返回string字符串的最大容量。Returns the maximum length the string can reach.但其实一般来说,我们很少去用这个函数,主要是对于最大容量的需求极少。

resize接口:

当我们建立好一个string对象之后,可以用这个函数去提前开辟一个空间,来防止多次扩容的影响。

int main()
{
	string s;
	s.resize(10);

	cout << s.size() << endl;
	return 0;

在上述代码中,我们将s的空间初始化为10。我们也可以借助这个函数去完成初始化。

int main()
{
	string s;
	s.resize(10, 'a');

	cout << s.size() << endl;
	cout << s << endl;
	return 0;
}

这段代码,就把s字符串的空间初始化为了10个‘a'。

capacity接口。

对于string的实现,我们姑且可以认为是由三部分组成:_str、_size、_capacity。其中的_capacity就是给_str开辟的容量。我们可以利用capacity接口去访问容量。

int main()
{
	string s;
	s.resize(10, 'a');
		
	cout << s.capacity() << endl;	//15
	cout << s.size() << endl;	//10
	return 0;
}

在上述的代码中,capacity被开辟到了15字节,size为10字节。

Part 3

reserve

reserve(保存、留存)不同于reverse(逆置)。reserve是用来申请容量的变化的。

int main()
{
	string s;
	s.resize(10, 'a');
		
	cout << s.capacity() << endl;	//15

	s.reserve(20);

	cout << s.capacity();  //31

	return 0;
}

可以看到,我们申请了20个字节,但是最终给我们了31个字节。这就跟reserve的特性有关。

当我们进行资源申请时,我们至少会得到n个字节的内存,甚至可能会多给我们一些内存。

clear接口

这个接口是用来清楚string的内容的。他会消除内容,但不会释放空间。

int main()
{
	string s;
	s.resize(10, 'a');
		
	cout << s.capacity() << endl;	//15

	s.clear();

	cout << s.size() << endl;
	cout << s.capacity();  //15

	return 0;
}

empty接口

就像是栈、堆等empy函数,这个函数也是用来判断有没有内容的。

shrink_to_fit接口

这个接口是用来减少容量的。它会将容量减少到适宜的空间大小。但一般我们不用用这个接口,毕竟减少容量再去扩容是由风险的

4.与成员权限相关的函数Element access:

1.[]的运算符重载

[]的重载支持在string中直接yong[]来访问内部内容。

2.at

at函数就是[]的另一种形式,也是用来访问内部元素的

int main()
{
	string s("hello world");
	
	cout << s.at(4) << endl;	//打印0
	
	return 0;
}

back函数:

返回最后一个字符的索引。

front函数:

返回第一个字符的索引。

5.修改器相关的函数Modifiers

Part 1:

1.operator+=

这是用来尾插的运算符重载。

int main()
{
	string s("hello world");
	
	s += "ace";

	cout << s;
	
	return 0;
}

完成了尾插ace。

2.append(附加)接口

append是用来追加字符串的。类似于+=

int main()
{
	string s("hello world");
	
	s.append(" hello");

	cout << s;
	
	return 0;
}

3.push_back接口

同理,也是完成+=的尾插操作的。

Part 2:

1.assign函数

Assigns(分派) a new value to the string, replacing its current contents。用一个新的值,去覆盖原来字符串的值。注意,此处的覆盖是完全覆盖,需要抹去之前的内容。

int main()
{
	string s("hello world");
	
	s.append(" hello");

	s.assign("ni hao");

	cout << s;
	
	return 0;
}

同理,我们可以用一部分值去替换。

int main()
{
	string s("hello world");
	
	string s2("nihao");

	s.assign(s2, 2, 2);

	cout << s;
	
	return 0;
}

用s2串的下标为2处,用长度为2的字符串去替换原来的字符串。

insert接口

类比栈和队列的insert,用来完成任意位置处数据的插入。

既可以在任意位置插入想要的内容,也可以在任意位置插入想要的某个字符串的子串(2)。

int main()
{
	string s("hello world");
	
	string s2("ni hao ");

	s.insert(0, s2);

	cout << s;
	
	return 0;
}

在上述代码中,我们将s2插入到s的头部。

当我们只想把hao插入到s中时,需要用(2)这个接口。

int main()
{
    string s("hello world");
    
    string s2("ni hao ");

    s.insert(0, s2, 3, 4);

    cout << s;
    
    return 0;
}

当然,也支持用迭代器去操作。

由于接口繁多,我们在使用的时候,查阅文档就好。同时,对于大部分修改内容、查阅内容的接口,都是支持迭代器的。

但是需要注意的是,insert能不用就不用,因为牵扯到数据的挪动,消耗极大。

replace接口

replace不同于assign,replace是“替换” ,而assign是“分配” 。

假如我们想让ni hao替换hello,只需要用(3)就可以

int main()
{
	string s("hello world");
	
	s.replace(0, 5, "ni hao ");

	cout << s;
	
	return 0;
}

当然,我们也可以直接传入对象来替换。

当然,replace与insert都是不建议经常使用的,否则内部会进行数据的挪动,消耗极大。

erase接口

如其他的erase,这个接口就是用来完成删除操作的。

1)传入位置与长度   2).传入一个迭代器,来删除一个字符   3).传入迭代器的一段区间去删除。

当然,由于数据的删除也是进行数据的挪动,因此也是消耗极大,尽量少使用。

int main()
{
	string s("hello world");
	
	s.erase(0, 3);
	cout << s << endl;
	
	return 0;
}

swap接口

这是用来交换字符串的内容的。

int main()
{
	string s("hello world");
	string s2("ni hao ma ");
	
	s.swap(s2);
	cout << s << endl;
	cout << s2 << endl;
	
	return 0;
}

当然,string还定义了一个全局的swap。

由于还存在模板swap,所以在string这个地方,就有三个swap可以使用。

但是由于存在全局的swap,并且参数匹配,string对象优先使用内部的swap,而不会使用全局swap。

这就省去了拷贝构造,大大提高了效率。

int main()
{
	string s("hello world");
	string s2("ni hao ma ");
	
	swap(s, s2);
	cout << s << endl;
	cout << s2 << endl;
	
	return 0;
}

pop_back接口

用来尾删的接口。

6.字符串操作接口函数String operations:

part 1:

c_str接口

我们都知道,C++是兼容C的语言,那我们在使用C的一些函数的时候,他只允许传入字符串,而不是传入自定义类型string对象,那该怎么办呢?这时候C++就提供了一个接口,c_str(),他能将string对象变成具有字符串一样行为的能力。

当我们使用strlen时,会报错,但是用这个接口之后,就不会报错。

同理,下一个接口

date接口

也是得到string字符串数据的,但是我们一般还是c_str()接口用的更多。

part 2:

get_allocator接口

get_allocator() 是 string 类的一个成员函数,它返回与 string 容器关联的分配器对象的副本。

分配器(Allocator)是C++标准库中的一个组件,它负责在容器(例如 vectorstringdequelist 等)中分配和释放内存。每个容器都有一个关联的分配器,用于管理容器的内存需求。

get_allocator() 函数允许你在容器之外使用相同的分配策略来分配内存,这样可以保证程序中内存分配的一致性。这在某些情况下是有用的,比如当你需要在容器之外分配与容器内部数据结构兼容的内存时,或者当你需要测量或控制内存使用时。

使用 get_allocator() 可以确保分配的内存与 string 内部使用的内存来自相同的内存池,这可以减少内存碎片,提高内存使用效率。

由于返回类型及其复杂,我们可以用auto去自动推导。

#include <iostream>
#include <string>

int main() {
    std::string s("Hello, world!");
    std::allocator<char> alloc = s.get_allocator();

    // 使用与string相同的分配器来分配内存
    char* p = alloc.allocate(10);

    // ... 使用分配的内存 ...

    // 释放内存
    alloc.deallocate(p, 10);

    return 0;
}

这是STL的六大组件:

copy接口

在C++中,string 类提供了一个 copy 成员函数,用于将字符串中的字符复制到指定的字符数组中。这个函数的用法比较直接,下面是一个简单的例子来展示如何使用 string 类的 copy 函数:

#include <iostream>
#include <string>

int main() {
    std::string str = "Hello, World!";

    // 创建一个字符数组来接收复制的字符串
    char buffer[20];

    // 使用copy函数将字符串复制到buffer中
    // 注意:copy函数不会自动添加空字符('\0')到复制的字符串末尾
    str.copy(buffer, 5, 0); // 从字符串的第一个字符开始复制5个字符

    // 手动添加空字符以终止字符串
    buffer[5] = '\0';

    std::cout << "Copied string: " << buffer << std::endl;

    return 0;
}

在这个例子中,str.copy(buffer, 5, 0) 调用将 str 中的前5个字符复制到 buffer 中。copy 函数的前两个参数分别是目标数组和要复制的字符数量。第三个参数是可选的,表示从源字符串的哪个位置开始复制(偏移量)。如果省略第三个参数,默认从字符串的开头开始复制。

需要注意的是,copy 函数不会自动在复制的字符串末尾添加空字符(\0),因此在上面的例子中,我们需要手动添加空字符来确保 buffer 是一个以空字符结尾的字符串。如果不添加空字符,buffer 可能包含未初始化的数据,这可能导致未定义的行为,特别是当你尝试使用 std::cout 或其他字符串处理函数时。

可以看得出来,这其实是比较鸡肋的函数。

part 3 : find系列

在C++中,string 类提供了一个 find 成员函数,用于在字符串中查找子字符串或字符。find 函数返回找到子字符串或字符的第一个实例的位置,如果没有找到,则返回一个特殊的常量 std::string::npos

使用 find 时,你应该总是检查返回值是否为 std::string::npos,以确定是否找到了匹配的字符。

他的返回类型是size_t,返回的是数据的下标。

int main()
{
	string s1("a b c d e f g");

	cout << s1.find("d e f") << endl;


	return 0;
}

在上述代码中,打印了第一次出现d e f出现时的下标,打印为6。

find是正向查找,那么rfind就是反向查找

查找的是对应的内容最后一次出现时的位置。

find_first_of接口

find_first_of 函数的返回值是一个 size_t 类型的值,它表示在字符串中找到的匹配字符的第一个实例的位置。这个位置是从字符串的开头开始的索引,从0开始计数(但不一定返回0)。

同时也提供了查找的位置。


int main()
{
    string str("Please, replace the vowels in this sentence by asterisks.");
    size_t found = str.find_first_of("aeiou");
    while (found != std::string::npos)      //找到时
    {
        str[found] = '*';

        //从found + 1继续查找
        found = str.find_first_of("aeiou", found + 1);  //found是找到的索引,found + 1,表示往后查找
    }

    cout << str << '\n';

    return 0;
}

在上述代码中,我们不断查找位置,然后替换成*

find_last_of 是倒着查找。find_first_not_of 、find _last_not_of则是找到不是指定内容的对应内容。

下一部分

substr接口

substr接口的返回类型是string类型,因此可以用来取出一部分去初始化另一个string对象。

int main()
{
    string str("Please, replace the vowels in this sentence by asterisks.");
    string s2(str.substr(0, 6));

    cout << s2 << endl;

    return 0;
}

compare接口

Compares the value of the string object (or a substring) to the sequence of characters specified by its arguments.它可以比较两个字符串或者两个字串,相同返回0,<0 和 >0是字符串的大小比较。

实例:

int main()
{
    string str("Please, replace the vowels in this sentence by asterisks.");
    string s2(str.substr(0, 6));

    cout << s2.compare(str) << endl;

    return 0;
}

7.全局的函数

除了我们知道的swap、<<   、>>  。还有加法,用来完成string类对象的相加,但是加法也得少用,由于是传值返回,消耗比较大。

关系操作符的重载

内部主要是一些常用的关系操作符,有 ==    >=    <=    <   >   != 。

输入控制:getline

这也是一个非成员的函数重载

我们都知道,在进行多次输入的时候,通常会把空格或者换行符作为两个string对象的间隔,此时当我们想要规定使用换行作为间隔时,可以用这个接口。

他的返回类型是i流,也就是说支持连续的输入。参数中的str是需要存储的字符串。

当我们想让s1存储 i love you  ,s2 存储 about you ?时,就不能使用简单的cin >> s1 >>s2;因为>>会把空格当成分隔符,这时候就需要getline。

int main()
{
    string s1, s2;

    getline(cin, s1);
    getline(cin, s2);

    //cin >> s1 >> s2;

    cout << s1 << endl;
    cout << s2 << endl;

    return 0;
}

三、扩展

关于utf-8和gbk的介绍:

UTF-8 和 GBK 是两种不同的字符编码方案,它们用于将字符转换为计算机可以理解的字节序列。
 

UTF-8
UTF-8(Unicode Transformation Format - 8-bit)是一种可变长度的编码方案,可以表示 Unicode 标准中的任何字符。UTF-8 使用 1 到 4 个字节来表示一个字符,具有以下特点:
- 对于 ASCII 字符(代码点在 0-127 之间),UTF-8 编码与 ASCII 编码相同,使用一个字节。
- 它向前兼容 ASCII,这意味着任何仅包含 ASCII 字符的文本文件也是有效的 UTF-8 编码。
- 它为每个字符提供了明确的字节顺序,因此不需要字节序标记(BOM)。
- 它是一种自同步编码,即使在传输过程中丢失或篡改了某些字节,也容易恢复同步。
- 它在全球范围内被广泛使用,特别是在互联网标准和开放源代码社区中。

GBK
GBK(汉字内码扩展规范)是一种双字节的编码方案,最初是为了在简体中文版的 Windows 操作系统中支持更多的汉字而设计的。GBK 编码具有以下特点:
- 它使用两个字节来表示一个汉字,兼容 GB2312 编码。
- GBK 编码包含了 GB2312 的所有字符,并且增加了更多的汉字和符号,总共可以表示超过 70000 个字符。
- GBK 在中国大陆地区被广泛使用,尤其是在一些旧的软件和文档中。
- GBK 不兼容 ASCII 编码,这意味着纯 ASCII 文本在 GBK 编码下可能会被错误地解释。

 区别
- 编码方式:UTF-8 是可变长度的,而 GBK 是固定长度的。
- 字符集:UTF-8 可以表示 Unicode 标准中的所有字符,而 GBK 主要用于中文字符和一些符号。
- 兼容性:UTF-8 向前兼容 ASCII,而 GBK 不兼容 ASCII。
- 使用范围:UTF-8 在全球范围内使用,特别是互联网和开放源代码项目。GBK 主要在中国大陆地区使用。
在选择编码方案时,如果需要支持多种语言和全球化的应用,通常会选择 UTF-8。如果应用主要面向中文用户,且需要兼容旧的系统和软件,可能会考虑使用 GBK。随着 Unicode 和 UTF-8 的普及,GBK 的使用正在逐渐减少。
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/696746.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

修复损坏的Excel文件比你想象的要简单,这里提供几种常见的修复方法

打开重要的Excel文件时遇到问题吗?Microsoft Excel是否要求你验证文件是否已损坏?Excel文件可能由于各种原因而损坏,从而无法打开。但不要失去希望;你可以轻松修复损坏的Excel文件。 更改Excel信任中心设置 Microsoft Excel有一个内置的安全功能,可以在受限模式下打开有…

阿里通义千问 Qwen2 大模型开源发布

阿里通义千问 Qwen2 大模型开源发布 Qwen2 系列模型是 Qwen1.5 系列模型的重大升级。该系列包括了五个不同尺寸的预训练和指令微调模型&#xff1a;Qwen2-0.5B、Qwen2-1.5B、Qwen2-7B、Qwen2-57B-A14B 以及 Qwen2-72B。 在中文和英文的基础上&#xff0c;Qwen2 系列的训练数…

前端开发部署:Visual Studio Code + vue

〇 说明 本教程全部采用默认安装路径&#xff0c;因为在进行自定义路径安装的时候&#xff0c;需要配置各种环境变量&#xff0c;在这个配置过程中&#xff0c;可能出现各种很混乱的问题。 一 安装Node.js 1 下载https://nodejs.org/en 2 按照默认NEXT执行 C:\Program Files…

C++ BFS相关题目

目录 图像渲染 岛屿数量 图像渲染 733. 图像渲染 vis就是标记1有没有被用过 符合条件的都放到队列里&#xff0c;每次出队列一个&#xff0c;判四个&#xff0c; 如果要改的值与当前的值相同直接返回 注意&#xff1a;image[x][y] prev要放在坐标判断的后面&#xff…

深度解析地铁票务系统的技术架构与创新应用

在城市交通体系中&#xff0c;地铁作为一种快速、便捷的公共交通方式&#xff0c;已经成为现代都市生活的重要组成部分。而地铁票务系统的技术架构&#xff0c;则是支撑地铁运营的核心之一。本文将深度解析地铁票务系统的技术架构与创新应用&#xff0c;从系统设计、数据管理、…

PE文件(七)扩大节合并节数据目录

添加shellcode有以下几种的方式&#xff1a; 1.直接在任意节的空白区添加代码 2.新增节添加代码 3.扩大最后一个节添加代码 4.合并节并添加代码 今天我们学习如何扩大节&#xff0c;合并节 扩大节 在上一节的学习中&#xff0c;我们可以通过上移NT头和节表覆盖DOS Stub以…

前端技术回顾系列 08|TS 泛型基础

在微信中阅读,关注公众号:CodeFit。 创作不易,如果你觉得这篇文章对你有帮助,请不要忘了 点赞、分享 和 关注 我的公众号:CodeFit,为我的持续创作提供一些动力。 上篇内容回顾:枚举(Enums) 在上篇文章中,我们详细回顾了 TypeScript 中的 枚举(Enums)。 枚举 是一…

spark第三篇sql

spark第三篇sql sparksql概述sparksql四大特性dataframe概述通过读取数据源创建dataFrameDataFrame常用操作DataSet将RDD转换为DataFrame代码开发sparksql 操作hivesqlsparksql读取mysql表中的数据sparksql将结果数据写入到mysql中 sparksql概述 1、sparksql发展史 shark为spa…

HTML静态网页成品作业(HTML+CSS)—— 环保主题介绍网页(5个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;未使用Javacsript代码&#xff0c;共有5个页面。 二、作品演示 三、代…

MQ解决的问题

系统中MQ能解决哪些问题&#xff1f; 1.不同语言的程序使用MQ通信 2.分布式&#xff0c;微服务&#xff0c;之间的通信&#xff0c;实现服务质检解耦 3.高并发实现销峰作用 4.实现异步&#xff0c;提高用户体验。

“程序员职业素养全解析:技能、态度与价值观的融合“

文章目录 每日一句正能量前言专业精神专业精神的重要性技术执着追求的故事结论 沟通能力沟通能力的重要性团队合作意识实际工作中的沟通案例结论 持续学习持续学习的重要性学习方法进步经验结论 后记 每日一句正能量 梦不是为想象&#xff0c;而是让我们继续前往。 前言 在数字…

Python数据分析与机器学习在电子商务推荐系统中的应用

文章目录 &#x1f4d1;引言一、推荐系统的类型二、数据收集与预处理2.1 数据收集2.2 数据预处理 三、基于内容的推荐3.1 特征提取3.2 计算相似度3.3 推荐物品 四、协同过滤推荐4.1 基于用户的协同过滤4.2 基于物品的协同过滤 五、混合推荐与评估推荐系统5.1 结合推荐结果5.2 评…

docker 下载镜像发现超时,加速加速方法

报错原因有可能旧的不能用了&#xff01;&#xff01;&#xff01;换下面的&#xff01;&#xff01;&#xff01; cat /etc/docker/daemon.json "registry-mirrors": ["https://bhu1x6ya.mirror.aliyuncs.com"] 编辑完成后执行以下命令重启docker即可&a…

企业官网:过时了,但又没完全过时

作为一名互联网冲浪级选手&#xff0c;我经常会看到一些有趣的产品。 这两年比较让我感兴趣的产品有「飞聊」、「即刻」及其旗下的「橙 App」等等&#xff0c;然后我就想上它们的官网看看。 虽然现在 app 是主流&#xff0c;但我非常不喜欢下载 app&#xff0c;一是麻烦&…

基于STM32开发的智能空气质量监控系统

⬇帮大家整理了单片机的资料 包括stm32的项目合集【源码开发文档】 点击下方蓝字即可领取&#xff0c;感谢支持&#xff01;⬇ 点击领取更多嵌入式详细资料 问题讨论&#xff0c;stm32的资料领取可以私信&#xff01; 目录 引言环境准备智能空气质量监控系统基础代码实现&…

【吊打面试官系列-Mysql面试题】MySQL_fetch_array 和 MySQL_fetch_object 的区别是什么 ?

大家好&#xff0c;我是锋哥。今天分享关于 【MySQL_fetch_array 和 MySQL_fetch_object 的区别是什么 &#xff1f;】面试题&#xff0c;希望对大家有帮助&#xff1b; MySQL_fetch_array 和 MySQL_fetch_object 的区别是什么 &#xff1f; 以下是 MySQL_fetch_array 和 MySQL…

高考志愿填报的技巧和方法

高考过后&#xff0c;最让家长和学生需要重视的就是怎样填报志愿。高考完和出成绩之前有一段很长的时间&#xff0c;而成绩出来之后往往报考的时间非常的紧张。在很短的时间内&#xff0c;高考的学生和他的家长要综合高考的成绩&#xff0c;考虑院校&#xff0c;专业&#xff0…

【9】openssl 代码调试

0x01 前言 最近在学习密码学&#xff0c;但是国密算法(SM2&#xff0c;SM3,SM4,SM9)的细节都在openssl项目里&#xff0c;当然一些国际算法也在。想着看下代码执行过程和理论结合起来。中间走了一些弯路&#xff0c;做个笔记。 0x02 openssl安装 一开始认为是不是直接下载好的…

万向节锁死(Gimbal Lock)

Gimbal Lock是一个常见的3D动画问题,主要由旋转顺序引起的。我来详细解释一下它的成因: 在三维空间中,任何旋转都可以分解为绕X,Y,Z三个轴的欧拉旋转(Euler Rotation)。每个轴的旋转是按照一定顺序进行的,比如XYZ或ZYX等。 理论上,通过这三个旋转值的组合,可以达到任意的空间…

14. RTCP 协议

RTCP 协议概述 RTCP&#xff08;Real-time Transport Control Protocol 或 RTP Control Protocol 或简写 RTCP&#xff09;&#xff0c;实时传输控制协议&#xff0c;是实时传输协议&#xff08;RTP&#xff09;的一个姐妹协议。 注&#xff1a;RTP 协议和 RTP 控制协议&#…