博客主页:小智_x0___0x_
欢迎关注:点赞收藏✍️留言
系列专栏:C++初阶
代码仓库:小智的代码仓库
C++中的string类是一个字符串容器类,它提供了一系列操作字符串的方法,例如连接、查找、删除、替换等。与C语言中的字符串不同,使用string类可以避免许多重复繁琐的操作,使得代码更加简洁和易于维护。另外,string类支持自动内存管理和动态扩容,可以根据需要动态地调整字符串的大小,这也是其与C语言中的字符数组相比的一个优势。
总结:
我们学习string类需要通过查文档来学习:string类文档介绍
前面提到了我们学习string类需要结合文档来学习,这里我们在文档中查询string类的构造。
文档下面也对这几个构造函数有解释>
创建一个空的字符串,长度为零个字符。
eg:
int main()
{
//构造一个空的字符串
string s1;
cout << s1 << endl;
return 0;
}
构造一个s字符串的拷贝。
eg:
int main()
{
//构造一个“hello C++”的字符串
string s0("hello C++");
cout << s0 << endl;
return 0;
}
拷贝构造函数,构造一个str字符串的副本。
eg:
int main()
{
//构造一个“hello C++”的字符串
string s0("hello C++");
//通过s0字符串拷贝构造出s2
string s2(s0);
cout << s0 << endl;
cout << s2 << endl;
return 0;
}
复制从字符位置pos开始
并跨越len个字符
的str部分(或者直到str的末尾,如果str太短或者len是string::npos)
eg:
int main()
{
//构造一个“hello World”的字符串
string s0("hello World");
string s3(s0, 8, 3);
cout << s0 << endl;
cout << s3 << endl;
return 0;
}
这里我们可以发现这个构造函数中有一个缺省值npos,我们可以查看这个npos的定义:
这里给npos赋值-1,其补码是全1,npos的类型又是size_t(无符号整型)发生了类型转换,把-1强转为size_t类型,所以将-1的补码全1转换为原码,又是无符号类型,所以原反补都相同,此时原码就成为了全1(整型最大值)。
Google翻译>
这个构造函数的作用是从s字符串中拷贝前n个字符赋给构造的对象。
eg:
int main()
{
string s5("C++ is very Good!", 12);
cout << s5 << endl;
return 0;
}
用字符c的n 个连续拷贝填充字符串。
eg:
int main()
{
string s6(10, 'c');
cout << s6 << endl;
return 0;
}
范围构造函数
以相同的顺序复制[first,last)范围内的字符序列。
eg:
int main()
{
string s0("hello C++");
string s7(s0.begin(), s0.begin() + 7);
cout << "s0:" << s0 << endl;
cout << "s7:" << s7 << endl;
return 0;
}
size的功能返回字符串的有效长度,以字节为单位。这是符合字符串内容的实际字节数,不一定等于它的存储容量。
eg:
int main()
{
string str("hello C++!");
cout << str.size() << endl;
return 0;
}
length的功能与size的功能相同,都是返回字符串的有效长度。
eg:
int main()
{
string str("hello C++!");
cout << str.size() << endl;
cout << str.length() << endl;
return 0;
}
返回当前为string 分配的存储空间的大小,以字节表示。
容量不一定等于字符串长度。它可以等于或更大,额外的空间允许对象在新字符添加到 string 时优化其操作。
我们在vs2019编译器下编译运行以下代码>
int main()
{
string str("hello C++!");
cout << str.size() << endl;
cout << str.length() << endl;
cout << str.capacity() << endl;
cout << str.max_size() << endl;
return 0;
}
可以看到capacity比实际的大小要大一点。
我们再来在g++编译器来运行上面的代码>
我们能看到g++编译器下的capacity和size的大小是相同的,两款编译器下的max_size也是不一样的,所以我们在平时几乎不会用max_size()。(max_size返回的是字符串可达到的最大长度)
测试字符串是否为空
返回字符串是否为空(即它的长度是否为0)
如果字符串长度为0则为true,否则为false。
清除字符串
擦除string 的内容,它变成一个空字符串(长度为0个字符)。
reserve(size_t n=0):为string预留空间,不改变有效元素个数,当reserve的参数小于
string的底层空间总大小时,reserver不会改变容量大小。
resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。
注意:
他的功能是可以返回pos位置的字符,也就是说可以通过 [ ]
来访问字符串中的某一个字符。
eg:
int main()
{
string str("hello C++");
cout << str << endl;
for (int i = 0; i < str.size(); i++)
{
cout << str[i];
}
cout << endl;
return 0;
}
可以看到我们通过str[i]也可以对str字符串进行遍历输出。
他也有函数重载,一个是被const修饰的一个不能被修改,一个没有被const修饰可以进行修改。这里我们举个栗子>
我们对一个字符串的每个字符都进行++处理,再来打印:
int main()
{
string str("hello C++");
cout << str << endl;
//对字符串的每个字符进行++
for (int i = 0; i < str.size(); i++)
{
str[i]++;
}
//通过[]对字符串进行遍历
for (int i = 0; i < str.size(); i++)
{
cout << str[i];
}
cout << endl;
return 0;
}
可以看到我们把原本的字符串进行了修改,当然如果是一个const修饰的对象调用operator[ ],他就会调用const修饰的operator[ ]函数,被const修饰的对象当然也不能被改变。
我们来看这样一段代码>
int main()
{
string s1("hello C+++");
string::iterator it = s1.begin();
while (it != s1.end())
{
cout << *it << " ";
++it;
}
cout << endl;
return 0;
}
其中s1.begin()
是指向的字符串的第一个字符,s1.end()
指向的是最后一个字符的下一个。
可以看到这个函数的功能是:
将迭代器返回到开头
返回指向字符串第一个字符的迭代器。
返回迭代器结束
返回指向字符串的结尾字符的迭代器。
理解了这两个迭代器函数。我们来运行上面代码看结果>
注:itertor是像指针一样的类型,有可能是指针,有可能不是指针。 具体是什么,我们以后再来探讨。
迭代器也是可以对数据进行修改的
int main()
{
string s1("hello C++");
cout << s1 << endl;
string::iterator it = s1.begin();
while (it != s1.end())
{
//通过迭代器对s1中的字符进行--
(*it)--;
++it;
}
/让s1.begin()重新指向开头位置
it = s1.begin();
while (it != s1.end())
{
cout << *it << " ";
++it;
}
cout << endl;
return 0;
}
如果想要倒着打印字符串也可以通过反向迭代器来进行操作>
int main()
{
string s1("hello C++");
cout << s1 << endl;
string::reverse_iterator it = s1.rbegin();
while (it != s1.rend())
{
cout << *it ;
++it;
}
cout << endl;
return 0;
}
上面代码中的s1.rbegin()
返回反向迭代器到反向开始,返回指向字符串最后一个字符,s1.rend()
返回反向迭代器到反向结束返回指向字符串第一个字符之前的理论元素的反向迭代。
rbegin()
返回一个反向迭代器,指向 string
对象中最后一个字符。
rend()
返回一个反向迭代器,指向 string
对象中第一个字符之前的位置。
C++11支持更简洁的范围for的新遍历方式
使用方法也是很简单,来看一段代码>
int main()
{
string s1("hello C++");
//for(char& ch : s1)
for (auto& ch : s1)
{
cout << ch;
}
cout << endl;
return 0;
}
上面for (auto& ch : s1)
的意思是依次取出s1
中的字符传给ch
,再对ch
进行打印。auto
是自动识别类型,当然我们也可以写成for (char& ch : s1)
,在我们日常中经常使用auto
自动识别类型。
当然也可以通过范围for对字符串中的内容进行修改。
int main()
{
string s1("hello C++");
for (auto& ch : s1)
{
ch++;
}
for (auto& ch : s1)
{
cout << ch;
}
cout << endl;
return 0;
}
范围for底层其实就是调用使用迭代器来完成>
通过反汇编代码我们可以看到在我们使用范围for的时候调用了两个迭代器函数,分别是begin()和end()。
将字符附加到字符串
将字符c附加到字符串的末尾,将其长度增加一。
int main()
{
string str("hello C+");
cout << str << endl;
str.push_back('+');
cout << str << endl;
return 0;
}
附加到字符串
通过在当前值的末尾附加额外的字符来 扩展字符串:
append也重载了许多个函数。我们这里介绍几个比较常用的。
这个函数的功能是追加str的拷贝。
eg:
int main()
{
string str1("hello ");
string str2("C++");
str1.append(str2);
cout << str1 << endl;
return 0;
}
追加str的子字符串的副本。子字符串是str的一部分,它从字符位置subpos开始并跨越sublen个字符(或者直到str的结尾,如果str太短或者sublen是string::npos)。
eg:
int main()
{
string str1("hello ");
string str2("C++");
str1.append(str2,0,2);
cout << str1 << endl;
return 0;
}
在字符串后面追加一个字符串。
eg:
int main()
{
string str1("hello ");
str1.append("C++");
cout << str1 << endl;
return 0;
}
在s指向的字符数组中追加前n个字符的副本。
eg:
int main()
{
string str1("hello ");
str1.append("C++! hello",3);
cout << str1 << endl;
return 0;
}
在str后面追加"C++ hello"
字符串中的前三个字符。
填充
追加字符c的n 个连续拷贝。
eg:
int main()
{
string str1("hello C");
str1.append(2,'+');
cout << str1 << endl;
return 0;
}
这个重载也是字符串追加的功能。可以追加string对象,可以追加字符串,也可以追加单个字符。
eg:
int main()
{
string name("John");
string family("Smith");
name += " K. "; // c-string
name += family; // string
name += '\n'; // character
cout << name;
return 0;
}
c_str()
是 string
类的成员函数,用于返回一个指向 string
对象中第一个字符的指针。该指针指向的字符数组以空字符结尾,可以用于与 C 风格字符串进行交互。以下是 c_str()
的用法和示例:
#include
#include
int main() {
std::string str = "hello";
const char *cstr = str.c_str();
std::cout << cstr << std::endl; // hello
return 0;
}
在上面的示例中,我们首先创建了一个 string
对象 str
,然后使用 c_str()
函数将其转换为 C 风格字符串,并将返回的指针存储在 const char *cstr
中。最后,我们使用 std::cout
输出了 cstr
指向的字符数组。需要注意的是,由于 c_str()
返回的指针指向的字符数组是以空字符结尾的,因此可以使用该指针与 C 风格字符串进行交互。
find()
是 string
类的成员函数,用于在字符串中查找指定的子串,并返回其位置。npos
是 string
类的静态成员变量,表示未找到指定子串时的返回值。以下是 find()
和 npos
的用法和示例:
#include
#include
int main() {
std::string str = "hello world";
std::string sub = "world";
std::size_t pos = str.find(sub);
if (pos != std::string::npos) {
std::cout << "Substring found at position " << pos << std::endl;
} else {
std::cout << "Substring not found" << std::endl;
}
return 0;
}
在上面的示例中,我们首先创建了一个 string
对象 str
,然后使用 find()
函数查找字符串 "world"
在 str
中的位置,并将返回值存储在 pos
中。接着,我们使用 if
语句判断 pos
是否等于 std::string::npos
,如果不等于,则说明找到了子串,输出其位置;否则说明未找到子串,输出提示信息 "Substring not found"
。
需要注意的是,find()
函数可以接受一个可选的参数,用于指定搜索起始位置。例如,如果我们想从 str
的第 7 个字符开始查找子串,可以将其作为 find()
函数的第二个参数传入:
std::size_t pos = str.find(sub, 7);
这样就会从第 7 个字符开始查找子串。
另外,需要注意的是,npos
的值是一个特殊值,表示未找到指定子串时的返回值。它的值是一个无符号整数,通常是一个非常大的数值,因此可以用于判断 find()
函数是否查找到了指定子串。
注意:
s.push_back(c) / s.append(1, c) / s += 'c'
三种的实现方式差不多,一般string类的+=
操作用的比较多,+=
操作不仅可以连接单个字符,还可以连接字符串。string
操作时,如果能够大概预估到放多少字符,可以先通过reserve
把空间预留好。我们上面一直在使用cout<
将字符串插入流
将符合str
值的字符序列插入到os
中。
此函数重载operator<<
使其行为如ostream::operator<<
中所述,适用于 C 字符串,也适用于字符串对象。
从流中提取字符串
从输入流is
中提取一个字符串,将序列存储在str
中,它被覆盖( str的先前值被替换)。
此函数重载operator>>
使其行为如istream::operator>>
中描述的用于 c 字符串,但应用于字符串对象。
每个提取的字符都附加到字符串,就好像它的成员push_back
被调用一样。
getline 是 C++ 中 string 类的一个成员函数,用于从输入流中获取一行数据。它的使用方法如下:
int main() {
string name;
cout << "请输入你的名字>";
getline(cin, name);
cout << "Hello " << name << endl;
return 0;
}
在上面的例子中,我们使用 getline 从标准输入流 std::cin 中读取一行数据,并将其存储到 line 变量中。然后,我们将这个字符串输出到标准输出流 std::cout 中。
也可以指定一个分隔符来代替默认的换行符 \n。例如,如果你想使用逗号作为分隔符,可以这样写:
int main() {
std::string name;
std::cout << "请输入你的名字>";
std::getline(std::cin, name, ',');
std::cout << "Hello " << name << std::endl;
return 0;
}
在上面的例子中,我们使用逗号作为分隔符来读取输入流中的一行数据。
relational operators
是 string
类的比较运算符,用于比较两个 string
类型的对象。
以下是 relational operators
的用法和示例:
==
运算符:判断两个 string
对象是否相等,如果相等则返回 true
,否则返回 false
。#include
#include
int main() {
std::string str1 = "hello";
std::string str2 = "world";
std::string str3 = "hello";
std::cout << std::boolalpha << (str1 == str2) << std::endl; // false
std::cout << std::boolalpha << (str1 == str3) << std::endl; // true
return 0;
}
!=
运算符:判断两个 string
对象是否不相等,如果不相等则返回 true
,否则返回 false
。#include
#include
int main() {
std::string str1 = "hello";
std::string str2 = "world";
std::string str3 = "hello";
std::cout << std::boolalpha << (str1 != str2) << std::endl; // true
std::cout << std::boolalpha << (str1 != str3) << std::endl; // false
return 0;
}
<
运算符:判断一个 string
对象是否小于另一个 string
对象,如果是则返回 true
,否则返回 false
。#include
#include
int main() {
std::string str1 = "hello";
std::string str2 = "world";
std::string str3 = "abc";
std::cout << std::boolalpha << (str1 < str2) << std::endl; // true
std::cout << std::boolalpha << (str1 < str3) << std::endl; // false
return 0;
}
>
运算符:判断一个 string
对象是否大于另一个 string
对象,如果是则返回 true
,否则返回 false
。#include
#include
int main() {
std::string str1 = "hello";
std::string str2 = "world";
std::string str3 = "abc";
std::cout << std::boolalpha << (str1 > str2) << std::endl; // false
std::cout << std::boolalpha << (str1 > str3) << std::endl; // true
return 0;
}
5.<=
运算符:判断一个 string
对象是否小于或等于另一个 string
对象,如果是则返回 true
,否则返回 false
。
#include
#include
int main() {
std::string str1 = "hello";
std::string str2 = "world";
std::string str3 = "abc";
std::cout << std::boolalpha << (str1 <= str2) << std::endl; // true
std::cout << std::boolalpha << (str1 <= str3) << std::endl; // false
return 0;
}
>=
运算符:判断一个 string
对象是否大于或等于另一个 string
对象,如果是则返回 true
,否则返回 false
。#include
#include
int main() {
std::string str1 = "hello";
std::string str2 = "world";
std::string str3 = "abc";
std::cout << std::boolalpha << (str1 >= str2) << std::endl; // false
std::cout << std::boolalpha << (str1 >= str3) << std::endl; // true
return 0;
}
上面的几个接口大家了解一下,string类中还有一些其他的操作,这里不一一列举,大家在需要用到时不明白了查文档即可。
今天我们认识了C++string类的用法
相信大家看完有一定的收获。
种一棵树的最好时间是十年前,其次是现在!
把握好当下,合理利用时间努力奋斗,相信大家一定会实现自己的目标!加油!创作不易,辛苦各位小伙伴们动动小手,三连一波~~~
,本文中也有不足之处,欢迎各位随时私信点评指正!
本节课的代码已上传gitee仓库!