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

C++ Primer 标准库类型string

欢迎阅读我的 【C++Primer】专栏

专栏简介:本专栏主要面向C++初学者,解释C++的一些基本概念和基础语言特性,涉及C++标准库的用法,面向对象特性,泛型特性高级用法。通过使用标准库中定义的抽象设施,使你更加适应高级程序设计技术。希望对读者有帮助!

在这里插入图片描述
在这里插入图片描述

目录

  • 3.2标准库类型string
    • 定义和初始化string对象
    • 直接初始化和拷贝初始化
    • string对象上的操作
    • 读写string对象
    • 读取未知数量的string对象
    • string的empty和size操作
    • string::size_type类型
    • 比较string对象
    • 为string对象赋值
    • 两个string对象相加
    • 字面值和string对象相加
    • 处理string对象中的字符
    • 处理每个字符?使用基于范围的for语句
    • 使用范围for语句改变字符串中的字符
    • 使用下标执行随机访问

3.2标准库类型string

标准库类型string表示可变长的字符序列,使用string类型必须首先包含string头文件。作为标准库的一部分,string定义在命名空间std中。接下来的示例
都假定已包含了下述代码:

include < string >
using std::string;
本节描述最常用的string操作。C++标准一方面对库类型所提供的操作做了详细规定,另一方面也对库的实现做出一些性能上的需求。因此,标准库类型对于一般应用场合来说有足够的效率。

定义和初始化string对象

如何初始化类的对象是由类本身决定的。一个类可以定义很多种初始化对象的方式,只不过这些方式之间必须有所区别:或者是初始值的数量不同,或者是初始值的类型不同。下面是几个例子:

string s1;      //默认初始化,s1是一个空字符串
string s2 = s1; //s2是s1的副本
string s3("value"); //s3是该字符串字面值的副本
string s4(10,'c');//s4的内容是cccccccccc

可以通过默认的方式初始化一个string对象,这样就会得到一个守的string,也就是说,该string对象中没有任何字符。如果提供了一个字符串字面值,则该字面值中除了最后那个空字符外其他所有的字符都被拷贝到新创建的string对象中去。如果提供的是一个数字和一个字符,则string对象的内容是给定字符连续重复若干次后得到的序列。

表3.1:初始化string对象的方式

string s1 默认初始化,s1是一个空串
string s2(s1) s2是s1的副本
string s2 = s1 等价于s2(s1),s2是s1的副本
string s3(“value”) s3是宇面值"value"的副木,除了字面值最后的那个空字符外
string s3=“value” 等价于s3(“value”),s3是字面值"value"的副本
string s4(n,‘c’) 把s4初始化为由连续n个字符c组成的串

直接初始化和拷贝初始化

C++语言有几种不同的初始化方式,通过string我们可以清楚地看到在这些初始化方式之间到底有什么区别和联系。如果使用等号(=)初始化一个变量,实际上执行的是拷贝初始化(copy initialization),编译器把等号右侧的初始值拷贝到新创建的对象中去。与之相反,如果不使用等号,则执行的是直接初始化(direct initialization)。

当初始值只有一个时,使用直接初始化或拷贝初始化都行。如果像上面的s4那样初始化要用到的值有多个,一般来说只能使用直接初始化的方式:

string s5="hiyan"; // 拷贝初始化
string s6("hiya"); //直接初始化
string s7(10,"c"); //直接初始化,s7的内容是cccccccccc

对于用多个值进行初始化的情况,非要用拷贝初始化的方式来处理也不是不可以,不过需要显式地创建一个(临时)对象用于拷贝:

string s8 = string(10,'c'); //拷贝初始化,s8的内容是cccccccccc

s8的初始值是string(10,‘c’),它实际上是用数字10和字符c两个参数创建出来的一个string对象,然后这个string对象又拷贝给了s8。这条语句本质上等价于下面的两条语句:

string temp(10,'c'); //temp的肉容是cccccccccc
string s8=temp;      //将temp指贝给s8

其实我们可以看到,尽管初始化s8的语句合法,但和初始化s7的方式比较起来可读性较差,也没有任何补偿优势。

string对象上的操作

一个类除了要规定初始化其对象的方式外,还要定义对象上所能执行的操作。其中,类既能定义通过函数名调用的操作,就像Sales_item类的isbn函数那样,也能定义<<、+等各种运算符在该类对象上的新含义.表3.2中列举了string的大多数操作。

表3.2:string的操作


os<<s 将s写到输出流os当中,返回os

is>>s 从is中读取字符串赋给s,字符串以空白分隔,返回is

getline(is,8) 从is中读取一行赋给s,返回is

s.empty() s为空返回true,否则返回false

s.size() 返回s中字符的个数

s[n] 返回s中第n个字符的引用,位置n从0计起

s1+s2 返回s1和s2连接后的结果

s1=s2 用s2的副本代替s1中原来的字符

s1 == s2 如果s1和s2中所含的字符完全一样,则它们相等;string对象的相等性判断对字母的大小写敏感

<,<=,>,>= 利用字符在字典中的顺序进行比较,且对字母的大小写敏感

读写string对象

使用标准库中的iostream来读写int、double等内置类型的值。同样,也可以使用IO操作符读写stritng对象:

//注意:要想编译下面的代码还需要适当的#include语句和using声明
int main()
{string s;//空字符串cin >> s;//将string对象读入s,遇到空白停止cout << s<<endl;//输出sreturn 0;
}

这段程序首先定义一个名为s的空string,然后将标准输入的内容读取到s中。在执行读取操作时,string对象会自动忽略开头的空白〔即空格符、换行符、制表符等并从第一个真正的字符开始读起,直到遇见下一处空白为止。

如上所述,如果程序的输入是" HelloWor1ld!"(注意开头和结尾处的空格),则输出将是"Hello",输出结果中没有任何空格。

和内置类型的输入输出操作一样,string对象的此类操作也是返回运算符左侧的运算对象作为其结果。因此,多个输入或者多个输出可以连写在一起:

string s1,s2;
cin >> s1 >> s2; //把第一个输入读到s1中,第二个输入读到s2中
cout << sl << s2 << endl;//输出两个string对象

假设给上面这段程序输入与之前一样的内容"hello world!",输出将是 “HelloWorld”。

读取未知数量的string对象

下面编写程序用于读取string对象:

int main()
{string word;while(cin >> word)    //反复读取,直至到达文件末尾cout << word << endl; //逐个输出单词,每个单词后面絮跟一个换行return 0;
}

在该程序中,读取的对象是string而非int,但是while语句的条件部分和之前版本的程序是一样的。该条件负责在读取时检测流的情况,如果流有效,也就是说没遇到文件结束标记或非法,那么执行while语句内部的操作。此时,循环佛将输出刚刚从标准输入读取的内容。重复若干次之后,一旦遇到文件结束标记或非法输入循环也就结束了。

使用getline读取一整行

有时我们希望能在最终得到的字符串中保留输入时的空白符,这时应该用getline函数代替原来的>>运算符。getline函数的参数是一个输入流和一个string对象,函数从给定的输入流中读入内容,直到遇到换行符为止(注意换行符也被读进来了),然后把所读的内容存入到那个string对象中去(注意不存换行符)。getline只要一遇到换
行符就结束读取操作并返回结果,哪怕输入的一开始就是换行符也是如此。如果输入真的一开始就是换行符,那么所得的结果是个空string。

和输入运算符一样,getline也会返回它的流参数。因此既然输入运算符能作为判断的条件,我们也能用getline的结果作为条件。例如,可以通过改写之前的程序让它一次输出一整行,而不再是每行输出一个词了:

int main()
{string line;//每次读入一整行,直至到达文件未尾while(getline(cin,1ine))cout << line << endl;return 0;
}

因为 line中不包含换行符,所以我们手动地加上换行操作符。和往常一样,使用 endl 结束当前行并刷新显示缓冲区。

触发getline函数返回的那个换行符实际上被丢弃了,得到的string对象中并不包含该换行符。

string的empty和size操作

顾名思义,empty函数根据string对象是否为宇返回一个对应的布尔值。和Sales_item类的isbn成员一样,empty也是string的一个成员函数。调用该函数的方法很简单,只要使用点操作符指明是哪个对象执行了empty函数就可以了。

通过改写之前的程序,可以做到只输出非空的行:

    //每次读入一整行,遇到空行直接跳过while(getline(cin,1ine))if(line.empty())cout << line << endl;

在上面的程序中,if语句的条件部分使用了逻辑非运算符(!),它返回与其运算对象相反的结果。此例中,如果size不为空则返回真。

size函数返回string对象的长度(即string对象中字符的个数),可以使用size函数只输出长度超过80个字符的行:

string line;
//每次读入一整行,输出其中超过80个字符的行
while(getline(ctn,line))if(line.size()>80)cout<<line<<endl;

string::size_type类型

对于size函数来说,返回一个int是合情合理的。但其实size函数返回的是一个string::size_type类型的值,下面就对这种新的类型作解释。

string类及其他大多数标准库类型都定义了几种配套的类型。这些配套类型体现了标准库类型与机器无关的特性,类型size_type即是其中的一种。在具体使用的时候,通过作用域操作符来表明名字size_type是在类string中定义的。

尽管我们不太清楚string::size_type类型的细节,但有一点是肯定的:它是一个无符号类型的值而且能足够存放下任何string对象的大小。所有用于存放string类的size函数返回值的变量,都应该是string::size_type类型的。

过去,string::size_type这种类型有点儿神秘,不太容易理解和使用。在C++11新标准中,允许编译器通过auto或者decltype来推断变量的类型:

auto len=line.size(); //len的类型是string::size_type

由于size函数返回的是一个无符号整型数,因此切记,如果在表达式中混用了带符号数和无符号数将可能产生意想不到的结果。例如,假设n是-个具有负值的int,则表达式s.size()< n 的判断结果几乎肯定是true。这是因为负值n会自动地转换成一个比较大的无符号值。

如果一条表达式中已经有了size()函数就不要再使用int了,这样可以邀免混用int和unsigned可能带来的问题。

比较string对象

string类定义了几种用于比较字符串的运算符。这些比较运算符逐一比较string对象中的字符,并且对大小写敏感,也就是说,在比较时同一个字母的大写形式和小写形式是不同的。相等性运算符(==和!=)分别检验两个string对象相等或不相等,string对象相等意味着它们的长度相同而且所包含的字符也全都相同。关系运算符<、<=、>、>=分别检验一个string对象是否小于、小于等于、大于、大于等于另外一个string对象。上述这些运算符都依照(大小写敏感的)字典顺序:

1.如果两个string对象的长度不同,而且较短string对象的每个字符都与较长string对象对应位置上的字符相同,就说较短string对象小于较长string对象。
2.如果两个string对象在树些对应的位置上不一致,则string对象比较的结果其实是string对象中第一对相异字符比较的结果。

下面是string对象比较的一个示例:

string str="Hello";
string phrase="HelloWorld";
string slang="Hiya";

根据规则1可判断,对象str小于对象phrase:根据规则2可判断,对象slang既大于str也大于phrase。

为string对象赋值

一般来说,在设计标准库类型时都力求在易用性上向内置类型看齐,因此大多数库类型都支持赋值操作。对于string类而言,允许把一个对象的值赋给另外一个对象:

string st1(10,’c‘),st2;//st1的内容是cccccccccc;st2是一个空字符串
st1 = st2;  //赋值:用st2的副本替换st1的内容//此时st1和st2都是空字符串

两个string对象相加

两个string对象相加得到一个新的string对象,其内容是把左侧的运算对象与右侧的运算对象串接而成。也就是说,对string对象使用加法运算符(+)的结果是一个新的string对象,它所包含的字符由两部分组成:前半部分是加号左侧string对象所含的字符、后半部分是加号右侧string对象所含的字符。另外,复合赋值运算符(+=)负责把右侧string对象的内容追加到左侧string对象的后面:

string s1 = "hello", s2="world";  // 在s1 和 s2 中都没有标点符号
string s3 = s1 + ", " + s2 + ‘\n’;
string s3 = s1 + s2;              //s3的内容是hello,world
s1 += s2;//等价于s1=s1+s2

字面值和string对象相加

即使一种类型并非所需,我们也可以使用它,不过前提是该种类型可以自动转换成所需的类型。因为标准库允许把字符字面值和字符串字面值转换成string对象,所以在需要string对象的地方就可以使用这两种字面值来替代。利用这一点将之前的程序改写为如下形式:

string s1 = "hello", s2 = "world"; //在s1和s2中都没有标点符号
string s3 = s1+ "," + s2 + '\n';

当把string对象和孙符字面值及字符串字面值混在一条语句中使用时,必须确保每个加法运算符(+)的两侧的运算对象至少有一个是string:

string s4 = s1 + ",";     //正确:把一个string对象和一个字面值相加<20
string s5 = "hello"+",";  //错误:两个运算对象都不是string
//正确;每个加法远算符都有一个运算对象是string
string s6 = s1 + "," + "world";
string s7 = "hello"+","+s2; //错误:不能把字面值直接相加

s4和s5初始化时只用到了一个加法运算符,因此很容易判断是否合法。s6的初始化形式之前没有出现过,但其实它的工作机理和连续输入连续输出是一样的,可以用如下的形式分组:

string s6=(s1+",") + "world";

其中子表达式s1+","的结果是一个string对象,它同时作为第二个加法运算符的左侧运算对象,因此上述语句和下面的两个语句是等价的:

string tmp = s1+",";//正确:加法运算符有一个运算对象是string
s6 = tmp+"world"; //正确;加法运算符有一个运算对象是string

另一方面,s7的初始化是非法的,根据其语义加上括号后就成了下面的形式:

strings7=("hello"+",")+S2;//错误:不能把字面值直接相加

很容易看到,括号内的子表达式试图把两个字符串字面值加在一起,而编详器根本没法做到这一点,所以这条语句是错误的。

因为某些历史原因,也为了与C兼容,所以C++语言中的字符串字面值并不是标准库类型string的对象。切记,字符串字面值与string是不同的类型

处理string对象中的字符

我们经常需要单独处理string对象中的字符,比如检查一个string对象是否包含空白,或者把string对象中的字母改成小写,再或者查看某个特定的字符是否出现等。

这类处理的一个关键问题是如何获取字符本身。有时需要处理string对象中的每一个字符,另外一些时候则只需处理某个特定的字符,还有些时候遇到某个条件处理就要停下来。以往的经验告诉我们,处理这些情况常常要涉及语言和库的很多方面。

另一个关键问题是要知道能改变某个字符的特性。在cctype头文件中定义了一组标准库函数处理这部分工作,表3.3列出了主要的函数名及其含义。

表3.3:cctype头文件中的函数

isalnum©当c是字母或数字时为真
isalpha©当c是孙母时为真
iscntz1©当c是控制守符时为真
tsdigit©当c是数字时为真
isgraph©当c不是空格但可打印时为真
islower©当c是小写字母时为真
isprint©当c是可打印字符时为真〔即c是空格或c具有可视形式)
ispunct©当c是标点符号时为真(即c不是控制字符、数字、字母、可打印空自中的-种)
isspace©当c是空自时为真(即e是空格、横向制表符、纵向制表符、回车符、换行符、迹纸符中的一种)
isupper©当c是大写字母时为真
isxdigit©当c是十六进制数字时为真
tolower©如果c是大写字母,输出对应的小写字母;否则原样输出c
toupper©如果c是小写字母,输出对应的大写字母;否则原样输出c

建议:使用C++版本的C标准库头文件

C++标准库中除了定义C++语言特有的功能外,也兼容了C语言的标准库。C语言的头文件形如name.h,C++则将这些文件命名为cname。也就是去除了.h后缎,而在文件名name之前添加了字母c,这里的c表示这是一个属于C语言标准库的头文件。

因此。cctype头文件和ctype.h头文件的内容是一样的,只不过从命名规范上来讲更符合C++语言的要求。特别的,在名为cname的头文件中定义的名字从属于命名空间std,而定义在名为.h的头文件中的则不然。

一般来说,C++程序应该使用名为ename的头文件而不使用name.h的形式,标准库中的名字总能在命名空间std中找到。如果使用.h形式的头文件,程序员就不得不时刻牢记哪些是从C语言那儿继承过来的,哪些又是C++语言所独有的。

处理每个字符?使用基于范围的for语句

如果想对string对象中的每个字符做点儿什么操作,目前最好的办法是使用C++11新标准提供的一种语句:范围for(range for)语句。这种语句遍历给定序列中的每个元素并对序列中的每个值执行树种操作,其语法形式是:

for(declaration : expyession)
stalenzen

其中,expression部分是一个对象,用于表示一个序列。declaration部分负责定义一个变量,该变量将被用于访问序列中的基础元素。每次迭代,deciaration部分的变量会被初始化为expression部分的下一个元素值。

一个string对象表示一个字符的序列,因此string对象可以作为范围for语句其中的expression部分。举一个简单的例子,我们可以使用范围for语句把string对象中的字符每行一个输出出来:

string str("somestring")
//每行输出str中的一个字符
for(auto c:str)          //对于str中的每个字符cout << c << endl;   //输出当前字符,后面紧跟一个换行符

for循环把变量c和str联系了起来,其中我们定义循环控制变量的方式与定义任意一个普通变量是一样的。此例中,通过使用auto关键字让编译器来决定变量c的类型,这里c的类型是char。每次迭代,str的下一个字符被拷贝给c,因此该循环可以读作"对于字符串str中的每个字符c,"执行某某操作。此例中的"某某操作"即输出一个字符,然后换行。

举个稍微复杂一点的例子,使用范围for语句和ispunct函数来统计string对象中标点符号的个数:

string s("HelloWorld!!");
//punct_cnt的类型和s.size的返回类型一样
decltype(s.size())punct_cnt=0;
//统计s中标点符号的数量
for(auto c:s)        // 对于s中的每一个字符if(itspunct(e))  // 如果该字符是标焯符号++punct_cnt; // 将标点符号的计数值加1
cout<<punct_cnt<<"punctuation characters in" << s << endl;

程序的输出结果将是:
3 punctuation characters in Hello World!!

这里我们使用dec1type关键字声明计数变量punctcnt,它的类型是s.size函数返回值的类型,也就是string::size_type。使用范围for语句处理string对象中的每个字符并检查其是否是标点符号。如果是,使用递增运算符给计数变量加1。最后,待范围for语句结束后输出统计结果。

使用范围for语句改变字符串中的字符

如果想要改变string对象中字符的值,必须把循环变量定义成引用类型。记住,所谓引用只是给定对象的一个别名,因此当使用引用作为循环控制变量时,这个变量实际上被依次绑定到了序列的每个元素上。使用这个引用,我们就能改变它绑定的字符。

新的例子不再是统计标点符号的个数了,假设我们想要把字符串改写为大写字母的形式。为了做到这一点可以使用标准库函数toupper,该函数接收一个字符,然后输出其对应的大写形式。这样,为了把整个string对象转换成大写,只要对其中的每个字符调用toupper函数并将结果再赋给原字符就可以了:

    strings("Hello World!!!");//转换成大写形式for(auto &c:8)//对于s中的每个字符(注意:c是引用)c=toupper(c);//c是一个引用,因此赋值语句将改变s中字符的值cout << s << endl;

上述代码的输出结果将是:

Hello World!!!

每次迭代时,变量c引用string对象s的下一个字符,赋值给c也就是在改变s中对应字符的值。因此当执行下面的语句时,

c= toupper(c);//c是一个引用,因此赋值语句将改变s中字符的值

实际上改变了c绑定的字符的值。整个循环结束后,str中的所有字符都变成了大写形式。

只处理一部分字符?

如果要处理string对象中的每一个字符,使用范围for语句是个好主意。然而,有时我们需要访问的只是其中一个字符,或者访问多个字符但遇到某个条件就要停下来。例如,同样是将字符改为大写形式,不过新的要求不再是对整个字符串都这样做,而仅仅他把string对象中的第一个字母或第一个单词大写化。

要想访问string对象中的单个字符有两种方式:一种是使用下标,另外一种是使用迭代器。

下标运算符([])接收的输入参数是string::size_type类型的值,这个参数表示要访问的字符的位置;返回值是该位置上字符的引用。

string对象的下标从0计起。如果string对象s至少包含两个字符,则s[0]是第1个字符、s[1]是第2个字符、s[s.size()-1]是最后一个字符。

string对象的下标必须大于等于0而小于s.size()。
使用超出此范围的下标将引发不可预知的结果,以此推断,使用下标访问>空string也会引发不可预知的结果。

下标的值称作"下标"或"索引",任何表达式只要它的值是一个整型值就能作为索引。不过,如果某个索引是带符号类型的值将自动转换成由string::size_type,表达的无符号类型。

下面的程序使用下标运算符输出string对象中的第一个字符:

if(!is.empty())//确保确实有字符需要输出cout << s[0] <<endl;  //输出s的第一个字符

在访问指定字符之前,首先检查s是否为空。其实不管什么时候只要对string对象使用了下标,都要确认在那个位置上确实有值。如果s为空,则s[0]的结果将是未定义的。

只要字符串不是常量,就能为下标运算符返回的字符赋新值。例如,下面的程序将字符串的首字符改成了大写形式:

string s("some string")
if(!s.empty())//确保s[0]的位置确实有字符s[0] = toupper(s[0]); // 为s的第一个字符赋一个新的值

程序的输出结果将是:

Some string

使用下标执行迭代
另一个例子是把s的第一个词改成大写形式:

    //依次处理s中的字符直至我们处理完全部字符或者遇到一个空白for(decltype(s.size()) index=0index!=s.size()&&!isspace(s[index]);++index)s[index]=toupper(s[index]);//将当前字符政成大写形式

程序的输出结果将是:

SOME string

在上述程序中,for循环使用变量index作为s的下标,index的类型是由decl1type关键字决定的。首先把index初始化为0,这样第一次迭代就会从s的首字符开始,之后每次迭代将index加1以得到s的下一个字符。循环体负责将当前的字母改写为大写形式。

for语句的条件部分涉及一点新知识,该条件使用了逻辑与运算符(&&)。如果参与运算的两个运算对象都为真,则逻辑与结果为真;否则结果为假。对这个运算符来说最重要的一点是,C++语言规定只有当左侧运算对象为真时才会检查右侧运算对象的情况。如此例所示,这条规定确保了只有当下标取值在合理范围之内时才会真的用此下标去访问字符串。也就是说,只有在index达到s.size()之前才会执行s[index]。随着index的增加,它永远也不可能超过s.size()的值,所以可以确保index比s.size()小。

提示:注意检查下标的合法性

使用下标必须确保其在合理的范围内,也就是说下标必须,大于等于0,而小于字符串的size()的值。一种简便易行的方法是,总是设下标的类型为string::size_type,因为此类型是无符号数,因为此类型是无符号数,可以确保下标不会小于0。此时,代码只需保证下标小于size()的值就史以了。

C++标准并不要求标准库检测下标是否合法。一旦使用了超出范围的下标,就会产生不可预知的结果。

使用下标执行随机访问

在之前的示例中,我们让字符串的下标每次加1从而按顺序把所有字符改写成了大写形式。其实也能通过计算得到某个下标值,然后直接获取对应位置的字符,并不是每次都得从前往后依次访问。

例如,想要编写一个程序把0到15之间的十进制数转换成对应的十六进制形式,只需初始化一个字符串令其存放16个十六进制"数字":

const string hexdigits = "0123456789RBCDEF";//可能的十六进制数字
cout << "Enter a series of numbers between 0 and 15"<<" separated by spaces. Hit ENTER when finished:"<<endl;
string result;       //用于保存十六进制的字符串
string::size_type n; //用于保存从输入流读取的数while(cin >> n)if(n<hexdigits.size()) //忽略无效输入result+= hexdigits[n];//得到对应的十六进制数字
cout<<"Your hex number is:"<<result<<endl;

假设输入的内容如下:

12 0 5 15 8 15

程序的输出结果将是:

Your hex number is: C05F8F

上述程序的执行过程是这样的:首先初始化变量hexdigits后其存放从0到F的十六进制数字,注意我们把hexdigits声明成了常量,这是因为在后面的程序中不打算再改变它的值。在循环内部使用输入值n作为hexdigits的下标, hexdigits[n]的值就是hexdigits内位置n处的字符。例如,如果n是15,则结果是F:如果n是12,则结果是C,以此类推。把得到的十六进制数字添加到result内,最后一并输出。

无论何时用到字符串的下标,都应该注意检查其合法性。在上面的程序中,下标n是string::size_type类型,也就是无符号类型,所以n可以确保大于或等于0。在实际使用时,还需检查n是否小于hexdigits的长度。

相关文章:

C++ Primer 标准库类型string

欢迎阅读我的 【CPrimer】专栏 专栏简介&#xff1a;本专栏主要面向C初学者&#xff0c;解释C的一些基本概念和基础语言特性&#xff0c;涉及C标准库的用法&#xff0c;面向对象特性&#xff0c;泛型特性高级用法。通过使用标准库中定义的抽象设施&#xff0c;使你更加适应高级…...

计算机网络安全与运维的关键 —— 常用端口全解析

目录 前言 常见端口分类及用途 20 端口&#xff08;FTP 数据传输&#xff09; 21 端口&#xff08;FTP 消息控制&#xff09; 22 端口&#xff08;SSH&#xff09; 23 端口&#xff08;Telnet&#xff09; 25 端口&#xff08;SMTP&#xff09; 53 端口&#xff08;DNS&…...

Vue.js 的介绍与组件开发初步

Vue.js 的介绍与组件开发初步 Vue.js 的介绍与组件开发初步引言第一部分&#xff1a;Vue.js 基础入门1.1 什么是 Vue.js&#xff1f;1.2 搭建 Vue.js 开发环境安装 Node.js 和 npm安装 Vue CLI创建新项目运行示例 1.3 第一个 Vue.js 示例 第二部分&#xff1a;Vue.js 组件开发基…...

【仿12306项目】通过加“锁”,解决高并发抢票的超卖问题

文章目录 一. 测试工具二. 超卖现象演示三. 原因分析四. 解决办法方法一&#xff1a;加synchronized锁1. 单个服务节点情况2. 增加服务器节点&#xff0c;分布式环境synchronized失效演示 方法二&#xff1a;使用Redis分布式锁锁解决超卖问题1. 添加Redis分布式锁2. 结果 方法三…...

wow-agent---task4 MetaGPT初体验

先说坑&#xff1a; 1.使用git clone模式安装metagpt 2.模型尽量使用在线模型或本地高参数模型。 这里使用python3.10.11调试成功 一&#xff0c;安装 安装 | MetaGPT&#xff0c;参考这里的以开发模型进行安装 git clone https://github.com/geekan/MetaGPT.git cd /you…...

MVANet——小范围内捕捉高分辨率细节而在大范围内不损失精度的强大的背景消除模型

一、概述 前景提取&#xff08;背景去除&#xff09;是现代计算机视觉的关键挑战之一&#xff0c;在各种应用中的重要性与日俱增。在图像编辑和视频制作中有效地去除背景不仅能提高美学价值&#xff0c;还能提高工作流程的效率。在要求精确度的领域&#xff0c;如医学图像分析…...

94,【2】buuctf web [安洵杯 2019]easy_serialize_php

进入靶场 可以查看源代码 <?php // 从 GET 请求中获取名为 f 的参数值&#xff0c;并赋值给变量 $function // 符号用于抑制可能出现的错误信息 $function $_GET[f];// 定义一个名为 filter 的函数&#xff0c;用于过滤字符串中的敏感词汇 function filter($img) {// 定义…...

LabVIEW如何有效地进行数据采集?

数据采集&#xff08;DAQ&#xff09;是许多工程项目中的核心环节&#xff0c;无论是测试、监控还是控制系统&#xff0c;准确、高效的数据采集都是至关重要的。LabVIEW作为一个图形化编程环境&#xff0c;提供了丰富的功能来实现数据采集&#xff0c;确保数据的实时性与可靠性…...

6 [新一代Github投毒针对网络安全人员钓鱼]

0x01 前言 在Github上APT组织“海莲花”发布存在后门的提权BOF&#xff0c;通过该项目针对网络安全从业人员进行钓鱼。不过其实早在几年前就已经有人对Visual Studio项目恶意利用进行过研究&#xff0c;所以投毒的手法也不算是新的技术。但这次国内有大量的安全从业者转发该钓…...

《Origin画百图》之脊线图

1.数据准备&#xff1a;将数据设置为y 2.选择绘图>统计图>脊线图 3.生成基础图形&#xff0c;并不好看&#xff0c;接下来对图形属性进行设置 4.双击图形>选择图案>颜色选择按点>Y值 5.这里发现颜色有色阶&#xff0c;过度并不平滑&#xff0c;需要对色阶进行更…...

linux 函数 sem_init () 信号量、sem_destroy()

&#xff08;1&#xff09; &#xff08;2&#xff09; 代码举例&#xff1a; #include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <semaphore.h> #include <unistd.h>sem_t semaphore;void* thread_function(void* arg) …...

Kafka架构

引言 Kafka 凭借其独树一帜的分区架构&#xff0c;在消息中间件领域展现出了卓越的性能表现。其分区架构不仅赋予了 Kafka 强大的并行计算能力&#xff0c;使其能够高效处理海量数据&#xff0c;还显著提升了系统的容灾能力&#xff0c;确保在复杂的运行环境中始终保持稳定可靠…...

刷题记录 动态规划-2: 509. 斐波那契数

题目&#xff1a;509. 斐波那契数 难度&#xff1a;简单 斐波那契数 &#xff08;通常用 F(n) 表示&#xff09;形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始&#xff0c;后面的每一项数字都是前面两项数字的和。也就是&#xff1a; F(0) 0&#xff0c;F(1) 1 F(n…...

RDP协议详解

以下内容包含对 RDP&#xff08;Remote Desktop Protocol&#xff0c;远程桌面协议&#xff09;及其开源实现 FreeRDP 的较为系统、深入的讲解&#xff0c;涵盖协议概要、历史沿革、核心原理、安全机制、安装与使用方法、扩展与未来发展趋势等方面&#xff0c; --- ## 一、引…...

设计模式的艺术-观察者模式

行为型模式的名称、定义、学习难度和使用频率如下表所示&#xff1a; 1.如何理解观察者模式 一个对象的状态或行为的变化将导致其他对象的状态或行为也发生改变&#xff0c;它们之间将产生联动&#xff0c;正所谓“触一而牵百发”。为了更好地描述对象之间存在的这种一对多&…...

【C语言设计模式学习笔记1】面向接口编程/简单工厂模式/多态

面向接口编程可以提供更高级的抽象&#xff0c;实现的时候&#xff0c;外部不需要知道内部的具体实现&#xff0c;最简单的是使用简单工厂模式来进行实现&#xff0c;比如一个Sensor具有多种表示形式&#xff0c;这时候可以在给Sensor结构体添加一个enum类型的type&#xff0c;…...

Baklib如何优化企业知识管理提升团队协作与创新能力分析

内容概要 在现代企业中&#xff0c;知识管理已经成为提升竞争力的关键因素之一。Baklib作为一种全面的知识管理解决方案&#xff0c;致力于帮助企业高效整合和运用内部及外部知识资源。它通过建立统一的知识管理框架&#xff0c;打破了部门之间的信息壁垒&#xff0c;实现了跨…...

Dubbo view

1、 说说Dubbo核心的配置有哪些&#xff1f; 答&#xff1a; 配置 配置说明 dubbo:service 服务配置 dubbo:reference 引用配置 dubbo:protocol 协议配置 dubbo:application 应用配置 dubbo:module 模块配置 dubbo:registry 注册中心配置 dubbo:monitor 监控中心配置 dubbo:pr…...

分享刷题过程中有价值的两道题目

小编在这里先祝大家新的一年里所愿皆得&#xff0c;万事顺意&#xff0c;天天开心&#xff01;&#xff01;&#xff01; 一.水仙花数 题目描述&#xff1a; 求100∼999中的水仙花数。若三位数ABCA^3B^3C^3&#xff0c;则称ABC为水仙花数。例如153&#xff0c;135333112527153&…...

蓝桥杯例题六

奋斗是一种态度&#xff0c;也是一种生活方式。无论我们面对什么样的困难和挑战&#xff0c;只要心怀梦想&#xff0c;坚持不懈地努力&#xff0c;就一定能够迈向成功的道路。每一次失败都是一次宝贵的经验&#xff0c;每一次挫折都是一次锻炼的机会。在困难面前&#xff0c;我…...

DeepSeek 详细使用教程

1. 简介 DeepSeek 是一款基于人工智能技术的多功能工具&#xff0c;旨在帮助用户高效处理和分析数据、生成内容、解答问题、进行语言翻译等。无论是学术研究、商业分析还是日常使用&#xff0c;DeepSeek 都能提供强大的支持。本教程将详细介绍 DeepSeek 的各项功能及使用方法。…...

《tcp/ip协议详解》,tcp/ip协议详解

TCP/IP协议&#xff08;Transmission Control Protocol/Internet Protocol&#xff09;是网络通信协议的一种&#xff0c;也被称为“Internet协议”&#xff0c;是Internet上运行的基本协议&#xff0c;广泛应用于各种网络环境和应用场合。以下是对TCP/IP协议的详细解析&#x…...

游戏引擎 Unity - Unity 设置为简体中文、Unity 创建项目

Unity Unity 首次发布于 2005 年&#xff0c;属于 Unity Technologies Unity 使用的开发技术有&#xff1a;C# Unity 的适用平台&#xff1a;PC、主机、移动设备、VR / AR、Web 等 Unity 的适用领域&#xff1a;开发中等画质中小型项目 Unity 适合初学者或需要快速上手的开…...

【数据结构】_时间复杂度相关OJ(力扣版)

目录 1. 示例1&#xff1a;消失的数字 思路1&#xff1a;等差求和 思路2&#xff1a;异或运算 思路3&#xff1a;排序&#xff0b;二分查找 2. 示例2&#xff1a;轮转数组 思路1&#xff1a;逐次轮转 思路2&#xff1a;三段逆置&#xff08;经典解法&#xff09; 思路3…...

[Java]异常

在程序运行时&#xff0c;如果遇到问题&#xff08;比如除以零、文件找不到等&#xff09;&#xff0c;程序会发生异常。异常就像是程序的“错误提醒”&#xff0c;当程序运行中出错时&#xff0c;它会停止&#xff0c;给出一个错误信息。我们可以通过异常处理来控制这些错误&a…...

【C++语言】卡码网语言基础课系列----13. 链表的基础操作I

文章目录 背景知识链表1、虚拟头节点(dummyNode)2、定义链表节点3、链表的插入 练习题目链表的基础操作I具体代码实现 小白寄语诗词共勉 背景知识 链表 与数组不同&#xff0c;链表的元素存储可以是连续的&#xff0c;也可以是不连续的&#xff0c;每个数据除了存储本身的信息…...

Vue.js组件开发-实现图片浮动效果

使用Vue实现图片浮动效果 实现思路 将使用Vue的单文件组件&#xff08;.vue&#xff09;来实现图片浮动效果。主要思路是通过CSS的transform属性结合JavaScript的定时器来改变图片的位置&#xff0c;从而实现浮动效果。 代码实现 <template><!-- 定义一个包含图片…...

自制Windows系统(十一、Windows11GUI)

开源地址&#xff1a;下载&#xff08;Work(Windows11gui).img&#xff09; 上图 部分代码&#xff1a; void init_screen8(char *vram, int x, int y) { int *fat; unsigned char c; struct MEMMAN *memman (struct MEMMAN *) MEMMAN_ADDR; boxfill8(vram, x, 136, 0, …...

索罗斯的“反身性”(Reflexivity)理论:市场如何扭曲现实?(中英双语)

索罗斯的“反身性”&#xff08;Reflexivity&#xff09;理论&#xff1a;市场如何扭曲现实&#xff1f; 一、引言&#xff1a;市场是镜子&#xff0c;还是哈哈镜&#xff1f; 在传统经济学中&#xff0c;市场通常被认为是一个理性、有效的反映现实的系统。按照经典经济学理论…...

力扣257. 二叉树的所有路径(遍历思想解决)

Problem: 257. 二叉树的所有路径 文章目录 题目描述思路复杂度Code 题目描述 思路 遍历思想(利用二叉树的先序遍历) 利用先序遍历的思想&#xff0c;我门用一个List变量path记录当前先序遍历的节点&#xff0c;当遍历到根节点时&#xff0c;将其添加到另一个List变量res中&…...