文章目录
1. 为什么学习string类?
在C++中,字符串是在字符数组的基础上发展的,我们可以使用字符数组来存储和处理字符串,但是字符数组有一些缺点,例如无法动态调整大小和处理字符串相关的操作都需要手动完成,这些问题可以使用C++的string类来解决,学习C++的string类具有很多好处,包括但不限于以下几点:
-
动态分配内存:string类使用动态分配内存来处理字符串,可以为你自动分配内存,也可以自动调整字符串的长度,而无需你自己手动分配内存或进行手动内存管理,这是字符数组无法做到的,因此使用string类编写程序可以大大简化代码的复杂度。
-
提高程序的安全性:C++的字符串类在处理字符串时,可以自动保证字符串的安全性,因为字符串长度是动态调整的,可以避免程序假溢出,而手动处理字符串时可能会出现越界问题,这会导致程序的不安全性。
-
更方便的字符串处理:string类提供了丰富的字符串处理函数,包括字符串比较、拼接、查找、截取、替换等,这些函数可以很方便地操作字符串,尤其对于文本处理等工作,使用string类可以使代码更加简洁、易读和易维护。
总之,学习C++的string类能够帮助我们更快、更简单、更有效地处理字符串。所以,不论在什么情况下,只要需要使用字符串,我们应该优先考虑使用C++的string类。
2. 标准库中的string类
2.1 string类(了解)
C++中的string类是一个定义在头文件中的标准C++类,用来处理字符串。与C语言的字符串不同,string类不需要手动指定大小,也不需要手动管理内存。string类会自动管理字符串的长度和动态分配内存。除此之外,string类还提供了丰富的字符串处理函数,可以方便地操作字符串。
C++类的文档介绍如链接中:C++类的文档介绍
得出string类的要点如下:
- string是表示字符串的字符串类
- 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。
- string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator>string;
- 不能操作多字节或者变长字符的序列。
2.2 string类的常用接口说明(注意下面我只讲解最常用的接口)
2.2.1 string类对象的常见构造
(constructor)函数名称 | 功能说明 |
---|---|
string() (重点) | 构造空的string类对象,即空字符串 |
string(const char* s) (重点) | 用C-string来构造string类对象 |
string(size_t n, char c) | string类对象中包含n个字符c |
string (const string& str);(重点) | 拷贝构造函数 |
示例如下:
#include<iostream>
#include<string>
using namespace std;
int main()
{
string s1;
string s2("hello world!");
string s3(12, 'T');
string s4(s2);
cout << s1 << endl;
cout << s2 << endl;
cout << s3 << endl;
cout << s4 << endl; // 调用 "string(const string& str)" 构造函数
return 0;
}
编译如下是很显而易见的:
2.2.2 string类对象的容量操作
函数名称 | 功能说明 |
---|---|
size(重点) | 返回字符串有效字符长度 |
capacity(重点) | 返回空间总大小 |
empty(重点) | 检测字符串释放为空串,是返回true,否则返回false |
clear(重点) | 清空有效字符 |
reserve (重点) | 为字符串预留空间** |
resize(重点) | 将有效字符的个数改成n个,多出的空间用字符c填充 |
string容器相关方法代码演示如下:
- size:
size()
是C++中string
类的一个成员函数,用于返回字符串中实际存储的字符数。该函数的语法如下:
size_t size() const;
使用size()
函数非常简单,只需要按以下方式调用:
#include<iostream>
#include<string>
using namespace std;
int main()
{
string str1 = "Hello World!";
cout << str1.size()<<endl; //输出字符串的长度(字符的数量)
cout<<str1.length()<<endl;//与size用法一样。
return 0;
}
在上述代码中,我们首先定义了一个名为str1
的字符串对象,并将其初始化为字符串"Hello World!",然后使用size()
函数输出该字符串中所包含的字符数量,即字符串的长度。
需要注意的是,size()
函数不会包括字符串末尾的空字符 '\0'
。
另外,由于size()
函数未改变字符串对象本身,因此可以在该函数后加上const
关键字,表示该函数不修改字符串对象,同时遵循C++中的常规实现方式。加上const
关键字的完整语法如下:
size_t size() const;
这样,就不需要担心在使用类似于迭代器等容器相关功能时会因为非常规行为而产生不必要的错误。
- capacity
capacity()
是C++中string
类的一个成员函数,用于返回当前string
对象所分配的内存缓冲区大小。该函数的语法如下:
size_t capacity() const;
使用capacity()
函数非常简单,只需要按以下方式调用:
#include<iostream>
#include<string>
using namespace std;
int main()
{
string str1 = "Hello World!";
cout << str1.capacity(); //输出字符串所分配缓冲区的大小
return 0;
}
在上述代码中,我们首先定义了一个名为str1
的字符串对象,并将其初始化为字符串"Hello World!",然后使用capacity()
函数输出该字符串所分配缓冲区的大小。
需要注意的是,capacity()
函数返回的是当前所分配的内存缓冲区大小,而非实际字符串长度
。该函数的返回值可能比实际字符串长度(即使用size()
函数返回的值)大,因为为了提高字符串的操作效率,string
类有时需要预先分配一些额外的内存空间。这些额外空间会在字符串长度发生变化(例如添加删除字符)时被利用。
与size()
函数类似,由于capacity()
函数未改变字符串对象本身,因此可以在该函数后加上const
关键字,表示该函数不修改字符串对象,同时遵循C++中的常规实现方式。加上const
关键字的完整语法如下:
size_t capacity() const;
这样,就不需要担心在使用类似于迭代器等容器相关功能时会因为非常规行为而产生不必要的错误。
- empty
empty()
是C++中string
类的一个成员函数,用于检测当前string
对象是否为空字符串(即长度为0)。该函数的语法如下:
bool empty() const;
使用empty()
函数非常简单,只需要按以下方式调用:
#include<iostream>
#include<string>
using namespace std;
int main()
{
string empty_str = "";
string non_empty_str = "Hello World!";
if (empty_str.empty())
cout << "empty_str is empty!" << endl;
else
cout << "empty_str is not empty!" << endl;
if (non_empty_str.empty())
cout << "non_empty_str is empty!" << endl;
else
cout << "non_empty_str is not empty!" << endl;
return 0;
}
在上述代码中,我们首先定义了两个字符串对象,分别为empty_str
和non_empty_str
。empty_str
是一个空字符串,而non_empty_str
是一个非空字符串"Hello World!"。然后使用empty()
函数分别检测两个字符串对象是否为空字符串。如果是空字符串,输出提示信息,否则输出另一个提示信息。
需要注意的是,empty()
函数的返回结果是一个布尔值,即true
表示当前string
对象是空字符串,false
表示当前string
对象不是空字符串。
另外,由于empty()
函数未改变字符串对象本身,因此可以在该函数后加上const
关键字,表示该函数不修改字符串对象,同时遵循C++中的常规实现方式。加上const
关键字的完整语法如下:
bool empty() const;
这样,就不需要担心在使用类似于迭代器等容器相关功能时会因为非常规行为而产生不必要的错误。
- clear
C++中的void clear()
函数用于清空字符串中的所有字符,将其变成一个空字符串。它的语法如下:
void clear();
使用clear()
函数非常简单,只需将其应用于需要清空的string
对象即可。例如下面这个例子:
#include <iostream>
#include <string>
using namespace std;
int main() {
string str = "Hello World!";
cout << "Before clear(): " << str << endl;
// 使用 clear() 清空字符串
str.clear();
cout << "After clear(): " << str << endl;
return 0;
}
在上述代码中,我们首先定义了一个字符串对象str
,并将其初始化为"Hello World!"
。然后在使用clear()
函数清空该字符串后输出。运行结果如下:
Before clear(): Hello World!
After clear():
可以看到,当clear()
函数应用于str
对象后,该对象成为空字符串。
需要注意的是,clear()
函数会直接修改被调用的字符串对象,因此调用该函数后字符串对象中的所有内容都会被清空。
- reserve
C++中的void reserve(size_t n=0)
函数可以指定string
对象的容量,参数n表示期望string
对象的容量为n个字符。这个函数主要是为了给string
对象分配内存空间,避免频繁的重新分配内存而提高程序执行效率。如果不指定参数,则默认容量为0。该函数的语法如下:
void reserve(size_t n = 0);
使用reserve()
函数也很简单,只需将其应用于所需的string
对象即可。例如:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str = "Hello World!";
str.reserve(100); // 指定容量为 100
cout << "Capacity of str: " << str.capacity() << endl; // 输出容量
return 0;
}
在上述代码中,首先定义了一个字符串对象str
,并将其初始化为Hello World!
。然后在使用reserve()
函数指定容量为100后,使用capacity()
函数输出其容量。capacity()
函数用于获取当前string
对象的容量,返回值为一个无符号整数。
需要注意的是,reserve()
函数只是分配内存空间,并不会改变字符串长度。因此如果指定的容量小于字符串长度,那么并不会将字符串截断为指定的长度。而如果指定的容量大于字符串长度,则会重新分配内存空间,并将容量重新设定为指定的容量大小。需要预留更多的内存空间
,可以有效减少字符串改变大小时的时间开销。
下面是一个使用reserve()
函数的例子:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str;
cout << "Before reserve: " << str.capacity() << endl;
str.reserve(100);
cout << "After reserve: " << str.capacity() << endl;
return 0;
}
运行结果如下:
Before reserve: 15
After reserve: 111//正如上面说的需要预留更多的内存空间,
//可以有效减少字符串改变大小时的时间开销。
- resize
C++中的void resize(size_t n)
和void resize(size_t n, char c)
函数用于改变string
对象的长度。其中,第一个函数将string
对象的长度设为n,第二个函数会同时改变其长度和内容,将string
对象的长度设为n,并使用字符c填充新的位置。这两个函数非常易用,只需将其应用于所需的string
对象即可。例如:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str = "Hello World!";
str.resize(5);
cout << "Shortened String: " << str << endl;
str.resize(11, '-');
cout << "Lengthened String: " << str << endl;
return 0;
}
编译运行结果如下:
Shortened String: Hello
Lengthened String: Hello------
在上述代码中,首先定义了一个字符串对象str
,并将其初始化为Hello World!
。然后在使用resize()
函数将其长度改为5后,使用cout
语句输出其压缩后的字符串。接着,在使用resize()
函数将其长度改为11并使用-
字符填充新位置后,再次使用cout
语句输出其扩展后的字符串。
需要注意的是,如果将size_t n
参数设置为比原字符串的长度更长的值,那么resize()
函数会自动使用空格字符填充字符串后面的位置,直到达到指定长度为止。如果append()函数追加字符串,则追加的字符串会覆盖掉空格字符。
最后需要提醒一下的是,使用resize()
函数可能会改变容量大小。如果需要提前分配一定的内存空间,可以使用reserve()
函数进行空间预分配。
2.2.3 string类对象的访问及遍历操作
函数名称 | 功能说明 |
---|---|
operator[ ] (重点) | 返回pos位置的字符,const string类对象调用 |
begin+end | begin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器 |
rbegin+rend | begin返回一个指向字符串最后一个字符的反向迭代器(即它的反向开头) + end返回一个反向迭代器,指向字符串第一个字符前面的理论元素(被认为是字符串的反向结束) |
范围for | C++11支持更简洁的范围for的新遍历方式 |
- operator[]
C++中的char& operator[] (size_t pos)
和const char& operator[] (size_t pos) const
是string
对象的重载运算符,用于访问和修改string
对象中特定位置的字符。其中,第一个函数返回一个char
类型的引用,可以用于修改该位置的字符。而第二个函数返回一个const char
类型的常量引用,只读访问该位置的字符。
这两个函数非常易用,只需将其应用于所需的string
对象即可。例如:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str = "Hello World!";
// 输出第6个字符
cout << "The 6th character is: " << str[5] << endl;
// 修改第6个字符
str[5] = '-';
cout << "Modified string: " << str << endl;
return 0;
}
在上述代码中,首先定义了一个字符串对象str
,并将其初始化为Hello World!
。然后在使用[]
运算符访问字符串中第6个位置的字符并输出,接着使用[]
运算符修改字符串中的第6个位置的字符,并再次使用cout
语句输出修改后的字符串。
需要注意的是,如果访问或修改的位置超过了string
对象的长度,则会导致运行时错误。因此,在使用[]
运算符时,需要保证所选定的位置位于string
对象的有效范围之内。
最后需要提醒一下的是,虽然[]
运算符非常方便,但是在使用时需要谨慎处理。如果需要在string
对象中插入或删除字符,可以考虑使用insert()
和erase()
等函数。
- begin + end
C++中的begin()
和end()
是string
对象的成员函数,用于返回一个指向字符串起始和结束位置的迭代器。其中,第一个函数返回一个iterator
类型的迭代器,可以用于修改其中的字符。而第二个函数返回一个const_iterator
类型的常量迭代器,只读访问其中的字符。
这两个函数使用起来非常简单,只需将其应用于所需的string
对象即可。例如:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str = "Hello World!";
// 使用迭代器遍历字符串并输出每个字符
cout << "Traversing the string using iterator:" << endl;
for(auto it = str.begin(); it != str.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
// 使用const_iterator遍历字符串并输出每个字符
cout << "Traversing the string using const_iterator:" << endl;
for(auto it = str.cbegin(); it != str.cend(); ++it)
//cbegin:返回指向字符串第一个字符的const_iterator。
//cend:返回一个const_iterator,指向字符串的后结束字符。
{
cout << *it << " ";
}
cout << endl;
return 0;
}
在上述代码中,首先定义了一个字符串对象str
,并将其初始化为Hello World!
。然后在使用begin()
和end()
函数返回该字符串的起始和结束位置,使用for
循环遍历字符串并输出每个字符。在第一个循环中使用iterator
类型的迭代器可以修改其中的字符,而在第二个循环中使用const_iterator
类型的常量迭代器只读访问其中的字符。
需要注意的是,如果使用iterator
类型的迭代器修改了其中的字符,string
对象中对应位置的字符也会被修改。因此,在使用iterator
类型的迭代器时需要谨慎处理。
最后需要提醒一下的是,begin()
和end()
函数返回的迭代器表示一个半开区间(左闭右开
),即[begin(), end())
,因此在使用时需要保证所选定的区间位于string
对象的有效范围之内。
- rbegin + rend
在C++中,rbegin()
和rend()
是string
类成员函数,它们分别返回指向字符串最后一个字符和第一个字符前一个位置的反向迭代器,用于从后向前遍历字符串。
下面是一个简单的示例:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str = "Hello";
cout << "Original string: " << str << endl;
// 反向输出字符串
cout << "Reversed string: ";
for(auto rit = str.rbegin(); rit != str.rend(); rit++)
{
cout << *rit;
}
cout << endl;
return 0;
}
在这个示例中,我们首先定义了一个字符串str
,然后使用rbegin()
和rend()
函数返回迭代器,倒序遍历字符串并输出每个字符。需要注意的是,反向迭代器的++
操作是向前移动一个位置,而非向后移动一个位置。
当需要在程序中从后向前遍历string
类型变量中的元素时,使用rbegin()
和rend()
函数可以非常方便地完成操作。
- 范围for
在C++11中引入了一种新的语法,称为范围for语句(Range-based for loop),可以更加方便地遍历容器中的元素。
范围for语句的语法如下所示:
for (variable : range)
{
statement(s);
}
其中,variable
表示迭代过程中用于存储容器中每个元素的变量名,range
表示被迭代的容器。
以vector
容器为例,下面是范围for语句的使用示例:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1("welcome to china!");
for (auto x : s1)
{
cout << x << " ";
}
return 0;
}
需要注意的是,范围for语句虽然简化了代码,但是在某些情况下,使用范围for语句可能会导致代码的性能下降。这是因为在范围for语句中,编译器会隐式地使用迭代器进行遍历,而迭代器遍历时需要进行指针的解引用,这可能会导致一些额外的开销。因此,在需要追求极致性能的场合,建议使用传统的for
循环或使用迭代器进行遍历。
2.2.4 string类对象的修改操作
函数名称 | 功能说明 |
---|---|
push_back | 在字符串后尾插字符c |
append | 在字符串后面追加字符串 |
operator+= | 在字符串后追加字符串str |
c_str | 返回C格式字符串 |
find +npos | 从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置 |
rfind | 从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置 |
substr | 在str中从pos位置开始,截取n个字符,然后将其返回 |
- push_back
在C++中,push_back是容器(例如string、vector、list、stack等)提供的一个成员函数,用于在容器的末尾添加一个元素。下面是一些使用push_back函数的常见示例:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1("my name is ");
s1.push_back('x');
cout << s1 << endl;
return 0;
}
编译结果如下:
需要注意的是,虽然push_back函数可以向容器末尾添加新元素,但是向容器中添加元素也有其他的方式,例如insert函数等。在使用容器操作时,需要根据不同的场景选择适合的操作方式。同时也需要注意,向容器中添加元素时可能会引起容器的大小变化,可能会影响指向容器元素的迭代器等,因此需要特别注意代码的正确性和性能。
- append
C++中,字符串类string
提供了许多很方便的操作方法,其中append()
函数可以在一个字符串的末尾进行拼接操作。下面是一些使用append()
函数的常见示例:
拼接两个字符串
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str1 = "Hello";
string str2 = "World";
str1.append(str2); // 将str2拼接在str1的末尾
cout << str1 << endl; // 输出"HelloWorld"
return 0;
}
拼接另一个字符串的一部分
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str1 = "Hello";
string str2 = "World";
str1.append(str2, 0, 3); // 将str2的前3个字符拼接在str1的末尾
cout << str1 << endl; // 输出"HelloWor"
return 0;
}
拼接一个字符数组
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str = "Hello";
char arr[] = "World";
str.append(arr); // 将字符数组arr拼接在str的末尾
cout << str << endl; // 输出"HelloWorld"
return 0;
}
需要注意的是,append()
函数可以支持多种参数类型,可以根据不同的需要选择适当的参数类型,实现字符串的拼接和修改。同时也需要注意,使用字符串的append()
函数操作字符串时需要注意代码的正确性和性能,特别是在使用循环拼接大量字符串时,需要谨慎处理,避免程序出现性能瓶颈和内存泄漏等问题。
- operator+=
C++中的string
类提供了operator+=
(加等于)操作符,可以在一个字符串的末尾进行拼接操作,其用法如下:
string& operator+=(const string& str);
string& operator+=(const char* s);
string& operator+=(char c);
其中,前两个操作符可以使用字符串或者字符数组拼接一个字符串,第三个操作符可以将一个字符拼接至一个字符串的结尾。
下面是一些使用operator+=
操作符进行拼接的示例:
拼接两个字符串
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str1 = "Hello";
string str2 = "World";
str1 += str2; // 将str2拼接在str1的末尾
cout << str1 << endl; // 输出"HelloWorld"
return 0;
}
拼接一个字符数组
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str = "Hello";
char arr[] = "World";
str += arr; // 将字符数组arr拼接在str的末尾
cout << str << endl; // 输出"HelloWorld"
return 0;
}
拼接一个字符
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str = "Hello";
char c = '!';
str += c; // 在字符串str的末尾添加一个感叹号字符
cout << str << endl; // 输出"Hello!"
return 0;
}
需要注意的是,在使用operator+=
操作符操作字符串时需要注意代码的正确性和性能,特别是在使用循环拼接大量字符串时,需要谨慎处理,避免程序出现性能瓶颈和内存泄漏等问题。
- c_str
在 C++ 中,string
类是一个常用的字符串操作类,它提供了许多方便、高效的字符串处理方法,如查找、截取、连接、比较等操作。c_str()
函数是string
类的一个重要成员函数,它的作用是返回一个指向string
对象中内容的字符数组指针,也称为 C 风格的字符串。当需要将string
对象与C风格字符串进行交互时,可以使用c_str()
函数将其转换为字符数组(以\0
结尾)。
下面我们来看一个简单的demo:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str = "Hello world!";
const char* p = str.c_str();
cout << "原始字符串:" << str << endl;
cout << "字符数组指针:" << p << endl;
return 0;
}
上述示例中,定义了一个字符串 str
,“Hello world!” 在输出的时候,分别输出了原始字符串和将其转化为的字符数组。由于 c_str()
函数返回的是指向常量字符的指针,因此 p
的类型为 const char*
。
更具体地,使用 c_str()
函数可以将 string
类型的对象转换为 C 风格的字符串,以方便和其他 C 函数进行交互。比如可以调用 C 标准库的 strcmp()
函数来比较两个字符串。
如果不转换,会出现如下报错:
#include <iostream>
#include <string>
#include <cstring>
using namespace std;
int main()
{
string str1 = "Hello";
string str2 = "World";
if (strcmp(str1.c_str(), str2.c_str()) == 0)
{
cout << "两个字符串相等" << endl;
}
else
{
cout << "两个字符串不相等" << endl;
}
return 0;
}
在上面的代码中,我们用 strcmp()
函数比较了两个字符串是否相等,它的返回值是0表示两个字符串相等,这里将 string
类型的对象转换为了 C 风格的字符串使用了c_str()
函数。
综上,c_str()
函数在 C++ 的字符串处理中是经常用到的,可以将 string
对象快速转换为 C 风格的字符串,以满足与其他 C 函数进行交互的需求。
- find + npos
在C++中,string
是一个非常强大、方便的字符串处理类。其中,find()
函数是它最常用的函数之一,其作用是在字符串中查找指定的子串,并返回其位置。在这个过程中,我们还可以用C++中的特殊变量npos
来表示一个不存在的位置。
npos
是在string
类中定义的一个静态常量,它表示一个无效的、特殊的位置值。在实际使用中,它通常用于表示某些特殊情况,比如查找的子串不存在的情况。它的值是一个特别大的无符号整数(一般为-1
)。
下面,我们来看一个简单的例子,演示如何使用find()
函数和npos
常量:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str = "Hello, world! This is a string.";
// 查找 "world" 子串
size_t pos = str.find("world");
if (pos != string::npos)
{
cout << "位置为 " << pos << endl;
}
else
{
cout << "'world' 不存在" << endl;
}
// 查找 "Java" 子串
pos = str.find("Java");
if (pos != string::npos)
{
cout << "位置为 " << pos << endl;
}
else
{
cout << "'Java' 不存在" << endl;
}
return 0;
}
在上述代码中,我们首先定义了一个字符串str
,然后使用find()
函数分别查找了子串"world"和"Java"。第一次查找成功了,输出了其位置;第二次查找失败了,输出了"‘Java’ 不存在"。需要注意的是,find()
函数返回的是被查找的子串第一次出现的位置,如果查找失败则返回string::npos
值。
可以看到,通过使用npos
常量,我们可以更加简洁地表示某个位置不存在。在实际应用中,npos
常量在字符串查找、替换等操作中都有大量使用,特别是当我们需要在字符串中查找多个子串时,find()
加npos
就具备了更加灵活的应用性。
- rfind
rfind()函数是C++ string库中的一个函数,它的作用是在指定的字符串中从后往前查找指定的子串,如果找到则返回该子串在原字符串中所在的起始位置,如果找不到,则返回一个特定的值。
rfind()函数的语法如下:
size_t rfind(const string &str, size_t pos = npos) const noexcept;
其中,str是要查找的子串,pos是从字符串的哪个位置开始往前查找,默认是从字符串最后一个字符开始查找,npos是一个无符号整数,代表一个不存在的位置值。
为了更好地理解,我们来看一个例子:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str = "Hello, world! This is a string.";
// 查找 "Hello" 子串
size_t pos = str.rfind("Hello");
if (pos != string::npos)
{
cout << "位置为 " << pos << endl;
}
else
{
cout << "'Hello' 不存在" << endl;
}
// 查找 "Java" 子串
pos = str.rfind("Java");
if (pos != string::npos)
{
cout << "位置为 " << pos << endl;
}
else
{
cout << "'Java' 不存在" << endl;
}
return 0;
}
pos != string::npos这个表达式的意思是判断pos是否等于
string::npos
,因为rfind()函数会返回一个无符号整数类型的值,如果查找的子串在原字符串中不存在,函数会返回一个特定的值string::npos
,npos是size_t
类型的常量,它的值是无符号整数最大值,一般是232-1或264-1,用来表示不存在的位置。
所以,当rfind()函数查找成功,返回子串在原字符串中的起始位置pos时,pos的值不等于string::npos
;而当查找失败时,返回的pos值等于string::npos
,此时pos与string::npos
相等,则表明查找子串不存在于原字符串中。
因此在程序中,使用pos != string::npos这个判断语句,可以很方便地判断查找子串是否存在于原字符串中。如果存在,则可以输出相应信息或对子串进行其它操作;如果不存在,则可以输出相应提示或进行其它处理。
在上述代码中,我们首先定义了一个字符串str
,然后使用rfind()函数分别查找了子串"Hello"和"Java"。第一次查找成功了,输出了其位置;第二次查找失败了,输出了"‘Java’ 不存在"。需要注意的是,rfind()函数返回的是被查找的子串最后一次出现的位置,如果查找失败则返回string::npos
值。
rfind()函数和find()函数的区别在于,rfind()函数是从字符串的末尾开始查找而find()函数是从字符串的头部开始查找,因此,如果想从后往前查找子串,建议使用rfind()函数。
- substr
substr()
是 C++string
类中常用的函数之一,它用于截取字符串中的一部分,从指定的位置开始截取指定长度的字符串,或从指定位置截取到字符串结尾。
substr()
函数的声明如下:
string substr(size_t pos = 0, size_t len = npos) const;
其中,pos
表示子串的起始位置,len
表示子串的长度。
如果只指定了 pos
参数,那么 substr()
函数就会从 pos
位置开始截取到字符串结尾的所有字符构成的子串;如果同时指定了 pos
和 len
参数,那么 substr()
函数就会从 pos
开始截取长度为 len
的子串。
需要注意的是,pos
的默认值是 0,len
的默认值是 string::npos
,string::npos
是一个在字符串中不存在的位置常量,所以如果只调用 substr()
函数,那么返回的是原字符串的完整拷贝。
下面是一个例子,演示了 substr()
函数的用法:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str = "Hello, world!";
// 从第 7 个字符开始截取长度为 5 的子串
string sub1 = str.substr(7, 5);
cout << sub1 << endl; // 输出 "world"
// 从第 7 个字符开始截取到字符串结尾的子串
string sub2 = str.substr(7);
cout << sub2 << endl; // 输出 "world!"
// 截取最后 6 个字符的子串
string sub3 = str.substr(str.size() - 6);
cout << sub3 << endl; // 输出 "world!"
return 0;
}
上述代码定义了一个字符串 str
,然后分别使用 substr()
函数截取了不同位置的子串,并输出了相应的结果。第一个 substr()
函数从第 7 个字符(从0开始计数)开始截取长度为 5 的子串,结果是 “world”;第二个 substr()
函数从第 7 个字符开始截取到字符串结尾的子串,结果是 “world!”;第三个 substr()
函数截取了最后 6 个字符的子串,结果同样是 “world!”。
总之,substr()
函数是一个很实用的字符串处理函数,可以快捷方便地截取指定位置的子串。
2.2.5 string类非成员函数
函数 | 功能说明 |
---|---|
operator+ | 尽量少用,因为传值返回,导致深拷贝效率低 |
operator>> | 输入运算符重载 |
operator<< | 输出运算符重载 |
getline | 获取一行字符串 |
relational operators | 大小比较 |
- operator+
C++ 中的string
类中提供了一个operator+
运算符,用于将两个string
对象合并成一个新的string
对象。这个运算符的使用非常简单,只需要将两个string
对象用+
运算符连接在一起就可以了,这样就会返回一个新的string
对象,它包含了两个输入字符串的内容。
下面是一个使用 operator+
运算符的例子:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str1 = "Hello, ";
string str2 = "world!";
string str3 = str1 + str2;
cout << str3 << endl;
return 0;
}
上述代码定义了三个 string
对象:str1
、str2
和 str3
,然后使用 +
运算符将 str1
和 str2
连接在一起,将结果赋值给 str3
,最后输出 str3
的内容,即 “Hello, world!”。
此外,operator+
运算符还支持将一个 string
对象和一个 C 风格字符串连接在一起,或将一个 char
类型的字符连接在一个 string
对象的末尾。例如:
// 将一个 C 风格字符串和一个 string 对象连接在一起
string str4 = str1 + "C++";
cout << str4 << endl; // 输出 "Hello, C++"
// 将一个字符连接在一个 string 对象的末尾
string str5 = "Hello";
str5 = str5 + '!';
cout << str5 << endl; // 输出 "Hello!"
总之,使用 operator+
运算符可以方便地将两个 string
对象合并成一个新的 string
对象,也可以将一个 string
对象和一个 C 风格字符串或一个字符连接在一起。这个运算符在字符串的拼接操作中非常实用。
- operator>>
operator>>
运算符是 C++ 中的输入流运算符,用于从标准输入设备(如键盘)或文件中读取数据,并将其存储到string
对象中,这个运算符在iostream
头文件中定义。
使用 operator>>
运算符读取 string
对象的语法非常简单。只需要使用 >>
运算符将输入流对象(cin
)和 string
对象(要读取的 string
对象)连接起来即可。例如:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str;
cout << "Please enter a string: ";
cin >> str;
cout << "You entered: " << str << endl;
return 0;
}
此代码段中,我们使用 cin
输入流对象和 >>
运算符将输入的字符串存储在 str
变量中。然后,我们使用 cout
输出流对象打印出输入的字符串。
除了从标准输入中读取数据之外,我们还可以使用 fstream
头文件中的流对象从文件中读取数据并将其存储在 string
对象中。
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main()
{
string str;
ifstream input_file("input.txt"); // 打开文件
input_file >> str;
cout << "File content: " << str << endl;
return 0;
}
在此的例子中,我们使用 input_file
流对象和 >>
运算符打开了一个名为 “input.txt” 的文件,并从该文件中读取第一个 string
,并将其存储在 str
变量中。最后输出 str
的值。
总之,operator>>
运算符是 C++ 中的输入流运算符,用于读取数据并将其存储到 string
对象中。我们可以从标准输入设备或文件中读取数据。
- operator<<
C++ 中的string
类提供了重载运算符<<
,使得我们可以通过输出流(如cout
)方便地打印字符串对象。这个运算符在<ostream>
头文件中声明,它重载了流插入运算符<<
。
使用 operator<<
运算符输出string
对象的语法非常简单,只需使用 <<
运算符连接输出流对象(如 cout
)和 string
对象即可。例如:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str = "Hello World!";
cout << str << endl;
return 0;
}
在这个例子中,我们使用 cout
输出流对象和 <<
运算符将 str
变量的值输出到标准输出流中。
如果要输出多个变量,则可以使用多个 <<
运算符组合在一起,例如:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str1 = "Hello";
string str2 = "World";
int num = 2021;
cout << str1 << " " << str2 << " " << num << endl;
return 0;
}
在此例子中,我们使用 cout
和 <<
运算符将 str1
、str2
和 num
输出到控制台屏幕上。
总之,重载的运算符 <<
是 string
类的一个非常方便的特性,它允许我们使用输出流将字符串对象快速和轻松地输出到控制台屏幕或者文件中。
- getline
C++ 中的string
类提供了一个成员函数getline()
用于从输入流(如cin
)中读取一行字符串并将其存储到string
对象中,这个函数在<string>
头文件中声明。
使用 getline()
函数读取输入流中的一行的语法很简单,只需要将输入流对象连接到该函数即可。例如:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str;
getline(cin, str);
cout << "You entered: " << str << endl;
return 0;
}
在这个例子中,我们使用 getline()
函数从标准输入流 cin
中读取一行字符串并将其存储在 str
变量中。然后我们使用 cout
输出流对象打印出读取的字符串。
此外,getline()
函数还接受一个可选参数 delimiter
,可以用来指定输入流中的分隔符。
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str;
getline(cin, str, ',');
cout << "You entered: " << str << endl;
return 0;
}
在这个例子中,我们使用 getline()
函数从标准输入流 cin
中读取一行字符串,
getline(cin, str, ',')
函数会读取输入流对象 cin
中的字符,直到遇到指定的分隔符,然后将其存储在 str
变量中。在这个例子中,指定的分隔符是逗号 ,
。
因此,当您运行这个程序时,它将等待您从控制台输入一行字符串,并在读取到逗号之前将其存储到 str
变量中。如果您输入的字符串中没有逗号,则将读取整个输入行,并将其存储到 str
变量中。
如果您想以其他字符作为分隔符,只需将 ','
替换为您想要的字符即可。例如,如果您想以空格作为分隔符,可以这样写:
getline(cin, str, ' ');
这将使 getline()
函数在读取输入流对象 cin
的字符时,直到遇到第一个空格字符为止,并将其存储在 str
变量中。
总之,getline()
函数是 C++ 中用于读取输入流中的一行字符串并存储到 string
对象中的方法,它允许我们指定输入流中的分隔符。
- relational operators
在 C++ 中,string
类的 relational operators(关系运算符)可以用于比较两个字符串的大小关系。这些运算符包括以下几个:
==
:判断两个字符串是否相等。!=
:判断两个字符串是否不相等。<
:判断第一个字符串是否小于第二个字符串。>
:判断第一个字符串是否大于第二个字符串。<=
:判断第一个字符串是否小于或等于第二个字符串。>=
:判断第一个字符串是否大于或等于第二个字符串。
这些运算符都可以使用 string
类对象进行比较,例如:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str1 = "hello";
string str2 = "world";
if (str1 == str2)
{
cout << str1 << " and " << str2 << " are equal." << endl;
}
else if (str1 < str2)
{
cout << str1 << " is less than " << str2 << endl;
}
else
{
cout << str1 << " is greater than " << str2 << endl;
}
return 0;
}
在这个例子中,我们定义了两个 string
类对象 str1
和str2
,并使用 relational operators 进行比较。由于 str1
的长度小于 str2
,因此在比较运算中,str1
小于 str2
,输出语句会显示 “hello is less than world”。
需要注意的是,使用 relational operators 比较字符串时,实际上是按字符逐个进行比较。如果两个字符串长度相等,则按照字典序逐个比较其字符的 ASCII 值;如果不等,则只比较较短字符串的字符。
总之,使用 relational operators 对 string
类对象进行比较可以快速判断两个字符串的大小关系,帮助我们实现字符串的排序和查找。