C++:STL - string

简介: C++:STL - string



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

所以在C++的STL库中提供了更加方便的string类。


basic_string

basic_string是C++中的一个标准库类,用于表示字符串。它是在标准库命名空间std中定义的。basic_string是一个模板类,根据需要可以用不同的字符类型来实例化,比如charwchar_t等。

basic_string下,有四个特化版本:

在世界上,计算机的字符有非常多的编码形式,比如UTF-8UTF-16UTF-32等等。每种字符编码形式,都有对应的解码规则,所以C++的>STL库提供了几种基本的类,而我们最常用的UTF-8万国码,就适用于string类。所以本博客讲解的也是string

基本上,string类提供了basic_string类的所有功能,并且提供了一些额外的操作,使得字符串的处理更加方便。因此,大多数情况下,我们会使用string类来处理字符串。


string

string的常见构造

string类提供了多个构造函数,用于创建字符串对象。下面详细讲解一下每个构造函数的用法和示例:

  1. 默认构造函数:string()
  • 创建一个空字符串对象。
  • 示例:
string str;

  1. 拷贝构造函数:string(const string& str)
  • 创建一个字符串对象,内容为另一个字符串对象str的拷贝。
  • 示例:
string str1("hello");
 string str2(str1); // "hello"

  1. 重载构造函数:string(const string& str, size_t pos, size_t len = npos)
  • 创建一个字符串对象,内容为字符串对象str的pos位置开始的len个字符。
  • 示例:
string str1("hello"); 
 string str2(str1, 1, 3); // "ell:
  • 注意:当不传入第三个参数时,其默认为npos,这个值是size_t的最大储存值,可以理解为是无穷大,也就是说,当不传入第三个参数,其会从pos位置开始拷贝到结束。

  1. 重载构造函数:string(const char* s)
  • 创建一个字符串对象,内容为字符串s
  • 示例:
string str("hello");
  1. 由于单参数构造函数支持类型转换,也可以写为:
string str = "hello";

  1. 重载构造函数:string(const char* s, size_t n)
  • 创建一个字符串对象,内容为字符串s的前n个字符。
  • 示例:
string str("hello", 3); //"hel"

  1. 重载构造函数:string(size_t n, char c)
  • 创建一个由n个字符c组成的字符串对象。
  • 示例:
string str(5, 'a'); // "aaaaa"

除了以上构造函数,std::string类还提供了其他一些成员函数,这里不做详解,只列举几个常用的。


string的输入输出

operator<<

string<<进行了重载,我们可以直接对string对象使用<<,此时会输出这个string对象的字符串值。

示例:

string s1 = "hello world";
string s2 = " csdn";
cout << s1 << endl; //"hello world"
cout << s1 << s2 << endl; //"hello world csdn"

c_str

string还提供了一个函数,用于读取string对象中的字符串。

string s = "hello world";
cout << s.c_str() << endl; // "hello world"

这样看起来,c_str有点多此一举了,明明可以直接输出,不用c_str效果也是一致的。

其实不然,通过c_str还是和直接输出有差别的。

c_str的返回值是一个字符串,而<<对于字符串的输出机制是遇到'\0'中止输出。而string类重载的<<输出机制则是完整输出整个字符串,如果遇到'\0',会把'\0'一起输出。

看一个案例:

string s = "hello world \0 csdn";
cout << s.c_str() << endl; // "hello world "
cout << s << endl; // "hello world 0 csdn"

可以看到,通过s.c_str()输出,这个字符串没有被完整输出,只输出了'\0'前面的部分。而通过<<重载的输出,把整个字符串也输出了。

不够要注意,不同编译器对'\0'的输出方式不同,有的是当作0输出,有的忽略,还有的当作空格输出。


operator>>

同样的,string也对>>进行了重载,我们可以使用>>输入字符串。

string s1;
string s2;
cin >> s1 >> s2; // 输入"hello world"
cout << s1 << endl; // "hello"
cout << s2 << endl; // "world"

但是如果我们输入的字符串中包含空格,那么>>就不是很好用了:

string s;
cin >> s1; // 输入"hello world"
cout << s1 << endl; // "hello"

由于输入字符串时,遇到空格会中止读取,所以包含空格的字符串不能用这个方式输入,此时就要用getline了.


getline

getline可以获取一整行的字符串,包括空格,只有遇到换行符的时候才会中止。

不过要注意getline不是string类的成员函数,但是是string头文件的普通函数。

所以使用时不需要使用对象.getline()这样的格式,而是直接使用。

其第一个参数是istream的对象,比如cin

第二个参数是输入到的string对象。

第三个参数决定遇到什么字符的时候停止读取,默认值为'\n',即换行时停止读取。

示例:

string s;
getline(cin, s); // "hello world csdn"
cout << s << endl; // "hello world csdn"

string访问及遍历

operator[ ] & at

operator[ ] 和 at都是通过下标访问的成员函数,两者作用几乎一致。

示例:

string s = "hello world";
cout << s[2] << endl; // 'l'
cout << s[8] << endl; // 'r'

示例:

string s = "hello world";
cout << s.at(2) << endl; // 'l'
cout << s.at(8) << endl; // 'r'

两者的参数完全一致,即pos下标,传入这个参数后,输出下标位置的字符。此外,他们各自都提供了const版本的重构,用于const对象。

两者唯一的区别就是越界的情况:

operator[ ]越界时,会通过assert报错,直接中止程序。

at越界时,会抛出异常。


font & back

fontback用于返回字符串的首尾字符。

要注意,其返回值为引用返回char&,所以对其修改时,是会影响到原先的字符串的。


如果我们想遍历一个字符串,可以通过operator[ ] 或者 at,利用for循环来遍历,但是STL库还提供了迭代器,用于遍历对象。

迭代器
begin & end

在C++的string类中,beginend是两个成员函数,用于返回字符串的起始和结束迭代器。

begin函数返回一个迭代器,该迭代器指向字符串的第一个字符。可以使用解引用运算符*获取迭代器指向的字符。例如,*str.begin()将返回字符串中的第一个字符。

end函数返回一个迭代器,该迭代器指向字符串结尾的下一个位置。因此end()函数返回的迭代器不指向字符串中的任何字符。可以将end函数返回的迭代器与其他迭代器进行比较,以确定迭代器是否已达到字符串的末尾。

两者的返回类型都是iterrtor或者const_iterrtor,这个是迭代器的类型,后面结合案例讲解。

使用beginend函数可以遍历字符串中的所有字符。

接下来演示如何使用beginend函数遍历字符串中的字符并输出它们:

string str = "Hello World";
  
// 使用begin()和end()函数遍历字符串中的字符
string::iterrtor it = str.begin()
while(it != str.end())
{
  cout << *it;
  ++it;
}

其中iterrtor是迭代器的类型,也是beginend的返回值类型。

当对象被const修饰时,那么其类型为const_iterrtor

平常可以直接用auto来代替这个类型。

所以以上代码可以写为:

string str = "Hello World";
  
// 使用begin()和end()函数遍历字符串中的字符
auto it = str.begin()
while(it != str.end())
{
  cout << *it;
  ++it;
}

输出结果为:Hello World

在上面的示例中,使用begin函数获取指向字符串开头的迭代器,并使用end函数获取指向字符串结尾的迭代器。使用while循环遍历字符串中的每个字符,通过解引用*it将迭代器指向的字符输出到终端。

需要注意的是,end函数返回的迭代器指向的位置是一个越界的位置,不应该去解引用该迭代器,因为它指向的是无效的位置。


rbegin & rend

迭代器不仅支持正向遍历,还支持反向遍历,此时就需要反向迭代器rbegin,rend了。其中r代表reverse,反转的意思。

rbegin是一个反向迭代器,用于指向容器的最后一个元素,并向前迭代。

rendrbegin的逆操作。rbegin返回容器的最后一个元素的迭代器,而rend返回容器的第一个元素之前的位置的迭代器,这是一个越界的位置,同样不允许修改。

下面是一个例子,

string s1 = "54321";
string:reverse_iterator rit = s.begin();
while (rit != str.rend()) {
  cout << *rit << " ";
  ++rit;
}

输出结果为:5 4 3 2 1

在上面的例子中,nums.rbegin()返回了指向最后一个元素的迭代器,而nums.rend()返回了指向第一个元素之前位置的迭代器。通过遍历这个范围内的元素,我们可以实现反向遍历。

注意:虽然这是一个反向迭代,但是我们的迭代器rit依然以++操作,而非--

当对象被const修饰时,那么rbeginrend的返回值类型为const_reverse_iterator


范围for

范围for循环也适用于string,其本质是基于迭代器的循环。

string s = "hello world";
for(auto ch : s)
{
  cout << ch;
}

以上代码输出"hello world"


string的容量操作

size & length

size返回当前字符串的长度。

length返回当前字符串的长度。

两者的作用完全一致,没有区别,那么为什么STL要设计两种方式来返回string对象的长度呢?

这是因为早期的string还不属于STL,使用length来表达长度,当string进入STL后,为了与其他的数据结构保持一致,都使用size来表示长度,于是又增加了一个size


max_size

string类中,max_size函数返回字符串对象的最大可能大小。这个值是由字符串对象所使用的内存分配器的最大分配限制决定的。

让我们通过一个例子来说明:

string myString;
   
cout << myString.max_size() << endl;

在这个例子中,我们创建了一个空的字符串对象myString,然后使用max_size函数来获取最大字符串大小。

请注意,max_size函数返回的是一个表示可能的最大大小的值,并不一定意味着可以确保分配这么多内存。实际上,这个值通常会受到计算机系统内存限制的限制。

例如,如果你的系统内存限制为4GB,那么max_size()函数返回的值可能是231-1 (约为2GB),因为这是系统能够分配的最大内存大小。


capacity

capacity函数用于返回字符串的当前容量(当前可以存储的最大字符数)。

string str = "Hello World";
    
cout << "当前容量:" << str.capacity() << endl;

clear

在string类中,clear函数用于清空字符串内容,并将字符串的长度设置为0。

下面是一个示例:

string str = "Hello, World!";
cout << "原始字符串: " << str << endl;
str.clear();
cout << "清空后的字符串: " << str << endl;

在上面的例子中,首先我们创建了一个名为strstring对象,并将其初始化为"Hello, World!"。然后我们使用clear函数将字符串内容清空,最后我们打印输出清空后的字符串,可以看到输出结果为空字符串。


empty

empty函数用于检查一个字符串是否为空,即字符串中是否没有任何字符。该函数返回一个bool类型的值。

示例:

string str1 = "Hello";
string str2 = "";
if (str1.empty()) 
{
  cout << "str1 is empty" << endl;
} 
if (str2.empty()) 
{
  cout << "str2 is empty" << endl;
}

在上面的代码中,我们创建了两个字符串变量str1str2,并分别初始化为"Hello"和""(空字符串)。

接着,我们使用empty()函数来检查这两个字符串是否为空。如果字符串为空,则输出相应的提示信息。

运行上述代码,输出结果为:

str1 is not empty
str2 is empty

可以看到,由于str1中有字符,所以str1.empty()返回false,而str2中没有字符,所以str2.empty()返回true。


reserve

reserve是一个用来预留字符串容量的方法。

reserve方法的目的是为了避免频繁的内存分配和释放操作,提高程序的效率。当我们使用string对象时,由于字符串的长度是可变的,所以在进行增删改等操作时,可能会导致内存重新分配,这会带来一定的时间开销。

为了避免这种情况,我们可以使用reserve方法来预先分配足够的内存空间。

示例:

string str;
cout << "初始容量: " << str.capacity() << endl;
    
str.reserve(100); // 预分配100个字符的容量
cout << "预分配后容量: " << str.capacity() << endl;
    
str = "Hello, World!";
cout << "赋值后容量: " << str.capacity() << endl;

输出结果为:

初始容量: 15
预分配后容量: 100
赋值后容量: 100

在这个示例中,我们首先创建了一个空的字符串str。通过调用capacity方法,我们可以查看当前字符串的容量。初始容量为15,这是字符串的初始默认容量。

接下来,我们调用reserve方法,预分配了100个字符的容量。通过再次调用capacity方法,我们可以看到预分配后的容量变为了100。

然后,我们将字符串赋值为"Hello, World!",并再次调用capacity方法。在这种情况下,预分配的容量没有发挥作用,因为字符串的长度并没有超过预分配的容量。所以赋值后的容量仍然为100。

通过使用reserve方法,我们可以在需要频繁操作字符串的情况下,提前预分配足够的内存空间,以避免不必要的内存分配和释放操作,从而提高程序的效率。


resize

resize函数用于改变字符串的大小。它可以增加或减少字符串的长度。

resize函数有两个参数,第一个参数是新的字符串长度,第二个参数是填充字符(可选)。如果新的长度小于当前长度,则字符串会被截断如果新的长度大于当前长度,则字符串会自动增加大小,并用指定的填充字符填充空白部分,如果没有选定字符,则默认用\0填充

下面的例子将演示如何使用resize函数。

string str = "Hello World!";
    
// 将字符串的长度改为10
str.resize(10);
cout << str << endl;
// 将字符串的长度改为15
str.resize(10);
cout << str << endl;
    
// 将字符串的长度增加到20,并用字符'+'填充空白部分
str.resize(20, '+');
cout << str << endl;

输出结果为:

Hello Worl
Hello Worl
Hello Worl++++++++

在上面的例子中,我们首先将字符串的长度改为10,超过10个字符的部分将被截断。

输出前10个字符Hello Worl

然后,我们将字符串的长度增加到15,此时没有传入第二个参数,此时用\0填充多出来的部分。

输出前15个字符,但是由于\0会被忽略,所以最后输出Hello Worl

最后将长度改为20,并用字符’+'填充空白部分。

输出Hello Worl++++++++

要注意的是,就算使用resizesize缩小,capacity容量大小不会减小,而是保持原本的大小


shrink_to_fit

shrink_to_fit函数用于将string对象的容量缩减为其实际大小。这个函数可以减少string对象的内存占用。

示例:

string str = "This is a long string that will be shortened.";
cout << "size before shrink: " << str.size() << endl;
cout << "capacity before shrink: " << str.capacity() << endl;
str.resize(10); // 缩减字符串的大小为10个字符
cout << "size after resize: " << str.size() << endl;
cout << "capacity after resize: " << str.capacity() << endl;
str.shrink_to_fit(); // 将字符串的容量缩减为实际大小
cout << "size after shrink: " << str.size() << endl;
cout << "capacity after shrink: " << str.capacity() << endl;

输出结果:

size before shrink: 45
capacity before shrink: 50
size after resize: 10
capacity after resize: 50
size after shrink: 10
capacity after shrink: 13

在上面的例子中,我们首先创建一个包含45个字符的string对象。然后,我们使用resize函数将字符串的大小缩减为10个字符。接下来,我们使用shrink_to_fit函数将字符串的容量缩减为实际大小。最后,我们通过调用sizecapacity函数来查看字符串的大小和容量。

可以看到,在调用shrink_to_fit函数之后,字符串的容量从之前的50缩减为13,与实际大小相匹配。


string的修改操作

push_back

push_back函数用于将字符添加到字符串的末尾。它接受一个字符作为参数,并将其追加到字符串的末尾。

示例:

string str = "Hello";
    
str.push_back(' '); // 添加一个空格字符到字符串的末尾
str.push_back('W');
str.push_back('o');
str.push_back('r');
str.push_back('l');
str.push_back('d');
    
cout << str << endl; // 输出 "Hello World"

在上面的示例中,我们首先创建了一个名为str的字符串,并将其初始化为"Hello"。然后我们使用push_back函数多次将字符添加到字符串的末尾。输出"Hello World"

注意:push_back函数只接受一个字符作为参数,如果传递一个字符串或其他类型的参数,将会引发编译错误


append

string类中的append方法用于将一个字符串追加到另一个字符串的末尾。它有多个重载版本,可以接受不同类型的参数作为输入。

基本使用:

string str1 = "Hello";
string str2 = " World!";
    
// 使用append方法将str2追加到str1的末尾
str1.append(str2);
    
cout << str1 << endl;  // 输出 "Hello World!"

在上面的例子中,我们首先创建了两个字符串变量str1str2,分别初始化为"Hello"和" World!"。然后,我们使用str1append方法将str2追加到str1的末尾。最后,我们将str1输出到控制台,得到的结果是"Hello World!"

append方法还可以接受其他类型的参数作为输入,例如const char*charstd::stringstd::initializer_list<char>等。下面是一些使用不同参数类型的案例:

string str1 = "Hello";
    
// 使用char*类型的参数
str1.append(" World!");
    
cout << str1 << endl;  // 输出 "Hello World!"
    
string str2 = "Hello";
// 使用char类型的参数
str2.append(3, 'w');
cout << str2 << endl;  // 输出 "Hellowww!"
string str3 = "Hello";
// 使用string类型的参数
string str4 = " World!";
str3.append(str4);
cout << str3 << endl;  // 输出 "Hello World!"

在上面的例子中,我们分别使用char*类型的参数、char类型的参数和string类型的参数来调用append方法。无论参数类型是什么,append方法都会将其追加到字符串的末尾,实现字符串的拼接操作。


operator+=

operator+=是用于将一个字符串追加到另一个字符串的末尾的运算符重载。

根据其三个重载可以看出来,operator+=可以追加string对象,字符串,字符三种类型,功能全面,语法简单,比appendpush_back好用的多。

示例:

追加string对象:

string s1 = "hello";
string s2 = " world";
//追加string对象
s1 += s2;
cout << s1 << endl; //"hello world”

追加字符:

string s = "hello world”;
s += '!';
cout << s << endl; //"hello world!”

追加字符串:

string s1 = "hello";
//追加string对象
s1 += " world";
cout << s1 << endl; //"hello world”

实际中推荐使用这个运算符重载替代appendpush_back


insert

insert函数可以用于在字符串中插入字符、子串或其他类型的数据。

通过函数重载可以看出,我们可以插入字符,字符串,string对象等。

插入字符串:

string str = "Hello, world!";
// 在指定位置插入字符串
str.insert(5, "!!!"); // 在位置5插入"!!!"
cout << str << endl;

输出结果为:Hello!!!, world!

插入字符:

string str = "Hello, world!";
// 在指定位置插入字符串
str.insert(5, 3, '!'); // 在位置5插入3个'!'
cout << str << endl;

输出结果为:Hello!!!, world!

注意:插入字符时,第二个参数是插入字符的个数,哪怕只插入一个字符,这个值也不能缺省。

插入string对象:

string str1 = "Hello, world!";
string str2 = "!!!";
// 在指定位置插入字符串
str.insert(5, str2); // 在位置5插入str2
cout << str << endl;

输出结果为:Hello!!!, world!


erase

erase函数可以用来删除字符串中的字符或子字符串。

首先,我们来看一个简单的示例,删除字符串中的一个字符:

string str = "Hello World";
str.erase(4); // 删除第5个字符,即空格
cout << str << endl; // 输出 "HelloWorld"

在这个例子中,我们创建了一个字符串"Hello World",然后使用erase函数删除了第5个字符,即字符串中的空格。最后输出的结果是"HelloWorld"

接下来,让我们来看一个稍微复杂一些的例子,删除字符串中的一个子字符串:

string str = "Hello World";
str.erase(6, 5); // 从第7个字符开始,删除5个字符,即"World"
cout << str << endl; // 输出 "Hello"

在这个例子中,我们同样创建了一个字符串"Hello World",然后使用erase函数删除了从第7个字符开始的5个字符,即字符串中的"World"。最后输出的结果是"Hello"

除了上述两种用法外,erase函数还可以接受迭代器作为参数,用来删除指定范围内的字符或子字符串。下面是一个例子:

string str = "Hello World";
string::iterator start = str.begin() + 6; // 第7个字符的迭代器
string::iterator end = str.begin() + 11; // 第12个字符的迭代器
str.erase(start, end); // 删除从第7个字符到第12个字符的子字符串,即"World"
cout << str << endl; // 输出 "Hello "

在这个例子中,我们同样创建了一个字符串"Hello World",然后使用迭代器指定了要删除的子字符串的范围,即从第7个字符到第12个字符。最后输出的结果是"Hello "(注意,这里输出结果后面还有一个空格,因为我们只删除了子字符串,而没有删除空格字符)。


replace

replace函数用于在字符串中替换指定的子字符串。

假设我们有一个字符串str,其内容为"Hello, World!“,我们想要将其中的"World"替换为"Universe”。我们可以使用string类的replace函数来实现。

string str = "Hello, World!";
    
cout << "原始字符串: " << str << endl;
    
    // 使用replace函数替换字符串
str.replace(str.find("World"), 5, "Universe");
    
cout << "替换后的字符串: " << str << endl;

在上述代码中,我们使用了string类的replace函数。该函数有三个参数:要替换的起始位置、要替换的字符个数、以及替换的字符串。

在本例中,我们使用str.find(“World”)来查找字符串中"World"的起始位置,返回值是一个整数表示位置。接着,我们指定要替换的字符个数为5,即"World"的长度。最后,我们将要替换的字符串指定为"Universe"。

编译并运行上述代码,输出结果应为:

原始字符串: Hello, World!
替换后的字符串: Hello, Universe!

可以看到,replace函数将原始字符串中的"World"替换为了"Universe",并输出了替换后的字符串。

此外,通过函数重载可以看出,其支持替换字符,通过迭代器替换字符串等等操作,这里不做展开,


swap

swap函数用于交换两个string对象之间的内容。

示例:

string str1 = "Hello";
string str2 = "World";
 
str1.swap(str2);
    
cout << "After swap:" << endl;
cout << "str1: " << str1 << endl;
cout << "str2: " << str2 << endl;

在上面的代码中,我们创建了两个string对象str1str2,并将它们初始化为"Hello""World"。然后我们使用swap函数将两个字符串的内容进行了交换。最后输出交换后的结果,我们可以看到str1的值变成了"World",而str2的值变成了"Hello"

在C++标准库中,有一个非成员的swap函数被定义在<std>头文件中,用于交换两个对象的值。

现在让我们将string类中的swap函数与std命名空间中的swap函数进行对比。

下面是一个示例,展示如何使用std中的swap()函数:

string str1 = "Hello";
string str2 = "World";
    
swap(str1, str2);
    
cout << "After swap:" << std::endl;
cout << "str1: " << str1 << endl;
cout << "str2: " << str2 << endl;

在这个例子中,我们使用了std命名空间中的swap函数,将str1str2的内容进行了交换。输出的结果与前一个例子是一样的。

但是std中的swap将两个对象完全交换了,这个过程创建了string类型的中间变量

而string中的swap并没有完全交换对象,只是交换了两个对象的指针,以及值,并没有创建string类型的中间变量

所以对于string对象,使用string自带的swap效率会更高


string的查找操作

find

find函数用于在字符串中查找子字符串的第一次出现。

find函数接受两个参数:

待查找的子字符串 str 或者字符c

开始搜索的位置 pos(可选,默认值为0)

返回值:

  • 如果找到了指定子串,返回它在原字符串中第一次出现的位置。
  • 如果未找到指定子串,返回string::npos

示例:

string str = "Hello World";
string subStr = "World";
// 使用find函数查找子字符串的位置
size_t index = str.find(subStr);
cout << "子字符串在位置 " << index << " 处被找到" << endl;

以上示例中,我们定义了一个字符串 str 和一个子字符串 subStr。然后使用find函数在str中查找subStr,并将返回的位置存储在index变量中。最后,我们通过判断index是否等于std::string::npos来确定是否找到了子字符串。

输出结果为:"子字符串在位置 6 处被找到",因为子字符串"World"在字符串"Hello World"的第6个位置开始出现。


refind

rfind用于在字符串中寻找指定子串的最后一次出现的位置,或者说是倒着查找

rfind函数接受两个参数:

待查找的子字符串 str 或者字符c

开始搜索的位置 pos(可选,默认为string::npos

返回值:

  • 如果找到了指定子串,返回它在原字符串中最后一次出现的位置。
  • 如果未找到指定子串,返回string::npos

下面是一个示例代码,演示了如何使用rfind函数:

string str = "hello world, hello C++";
string subStr = "hello";
size_t found = str.rfind(subStr);
cout << "Found at position: " << found << endl;

运行结果为:

Found at position: 13

在上面的示例中,我们定义了一个字符串str,然后使用rfind函数在字符串中查找子串"hello"的最后一次出现的位置。由于"hello"字符串最后一次出现在位置13,所以found的值为13,并输出"Found at position: 13"


sub_str

substr用于提取字符串的子串。该函数可以接受两个参数,起始索引位置和子串的长度。

示例:

string str = "Hello, world!";
// 提取从索引位置3开始的子串
string sub1 = str.substr(3);
// 输出子串
cout << sub1 << endl; // 输出为 "lo, world!"
// 提取从索引位置7开始长度为5的子串
std::string sub2 = str.substr(7, 5);
// 输出子串
cout << sub2 << endl; // 输出为 "world"

在上面的示例代码中,我们首先创建了一个字符串 str,然后使用 substr() 函数提取了两个不同的子串。第一个 substr() 函数调用中,指定了起始索引位置为3,没有指定长度,因此提取从索引位置3开始到字符串末尾的子串。第二个 substr() 函数调用中,指定了起始索引位置为7和长度为5,因此提取了从索引位置7开始长度为5的子串。

运行程序,输出结果为:

lo, world!
world

可以看到,substr() 函数成功提取了指定位置和长度的子串,并将其存储在新的字符串变量中。


find_first_of & find_first_not_of

find_first_of函数用于在字符串中查找第一个与指定字符序列中的任意字符匹配的字符的位置

find_first_of函数有多个重载形式,其中最常用的形式如下:

size_t find_first_of (const string& str, size_t pos = 0) const;

这个函数接受一个字符串参数str,并从指定的位置pos开始搜索。find_first_of函数会在调用对象的字符串中查找第一个与str中的任意字符匹配的字符,并返回其位置。如果找不到匹配的字符,则返回string::npos

下面是一个示例:

string str = "Hello World";
string chars = "aeiou";
// 在str中查找第一个出现在"aeiou"中的字符
size_t pos = str.find_first_of(chars);
 
cout << "第一个元音字母出现在位置:" << pos << endl;
cout << "字符为:" << str[pos] << endl;

输出结果为:

第一个元音字母出现在位置:1
字符为:e

在上面的示例中,我们定义了一个字符串str和一个字符串chars,其中chars包含了要查找的字符序列,即元音字母。然后,我们使用find_first_of函数在str中查找第一个出现的元音字母的位置,并将结果存储在变量pos中。最后,我们根据pos的值输出结果。

注意,find_first_of函数还有其他的重载形式,可以接收更多参数,如指定搜索的区间等,这里不做详解。以上是其中最常用的一种形式,更详细的使用方式可以参考C++的官方文档或相关教程。

find_first_not_of则是查找在目标字符串中的字符。

比如:

size_t pos = str.find_first_not_of("aeiou");

就是在str中查找第一个不存在于"aeiou"中字符。


find_last_of & find_last_not_of

first对应的就是last,也就是从后往前找,性质是一样的,这里简单讲解一个案例:

string str = "Hello World";
string chars = "aeiou";
// 在str中查找最后一个出现在"aeiou"中的字符
size_t pos = str.find_last_of(chars);
 
cout << "最后一个元音字母出现在位置:" << pos << endl;
cout << "字符为:" << str[pos] << endl;

输出结果为:

最后一个元音字母出现在位置:7
字符为:o

相关文章
|
27天前
|
C++ 容器
|
18天前
|
存储 程序员 C++
C++常用基础知识—STL库(2)
C++常用基础知识—STL库(2)
60 5
|
17天前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
17 1
|
18天前
|
存储 自然语言处理 程序员
C++常用基础知识—STL库(1)
C++常用基础知识—STL库(1)
44 1
|
27天前
|
C++ 容器
|
27天前
|
C++ 容器
|
27天前
|
存储 C++ 容器
|
27天前
|
算法 安全 Linux
【C++STL简介】——我与C++的不解之缘(八)
【C++STL简介】——我与C++的不解之缘(八)
|
20天前
|
算法 数据处理 C++
c++ STL划分算法;partition()、partition_copy()、stable_partition()、partition_point()详解
这些算法是C++ STL中处理和组织数据的强大工具,能够高效地实现复杂的数据处理逻辑。理解它们的差异和应用场景,将有助于编写更加高效和清晰的C++代码。
16 0
|
22天前
|
C语言 C++
深度剖析C++string(中)
深度剖析C++string(中)
43 0