string类的使用
- 什么是string类?
- string构造
- string();
- string (const char* s);
- string (const string& str);
- string (const string& str, size_t pos, size_t len = npos);
- string (const char* s, size_t n);
- string (size_t n, char c);
- template < class InputIterator >string (InputIterator first, InputIterator last);
- string类对象的容量操作
- size
- length
- capacity
- empty
- clear
- reserve
- resize
- string类对象的访问及遍历操作
- operator[]
- iterator迭代器
- reverse_iterator反向迭代器
- 范围for
- 范围for底层实现原理
- string类对象的修改操作
- push_back()
- append()
- string& append (const string& str);
- string& append (const string& str, size_t subpos, size_t sublen);
- string& append (const char* s);
- string& append (const char* s, size_t n);
- string& append (size_t n, char c);
- operator +=
- c_str()
- find + npos
- string类非成员函数
- getline
- relational operators (string)字符串比较函数
- 🍀小结🍀
🎉博客主页:小智_x0___0x_
🎉欢迎关注:👍点赞🙌收藏✍️留言
🎉系列专栏:C++初阶
🎉代码仓库:小智的代码仓库
什么是string类?
C++中的string类是一个字符串容器类,它提供了一系列操作字符串的方法,例如连接、查找、删除、替换等。与C语言中的字符串不同,使用string类可以避免许多重复繁琐的操作,使得代码更加简洁和易于维护。另外,string类支持自动内存管理和动态扩容,可以根据需要动态地调整字符串的大小,这也是其与C语言中的字符数组相比的一个优势。
- 字符串是表示字符序列的类
- 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作单字节字符字符串的设计特性。
- string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型(关于模板的更多信息,请参阅basic_string)。
- string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_traits和allocator作为basic_string的默认参数(根于更多的模板信息请参考basic_string)。
- 注意,这个类独立于所使用的编码来处理字节:如果用来处理多字节或变长字符(如UTF-8)的序列,这个类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。
总结:
- string是表示字符串的字符串类
- 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。
- string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator>string;
- 不能操作多字节或者变长字符的序列。在使用string类时,必须包含#include头文件以及using namespace std;
我们学习string类需要通过查文档来学习:string类文档介绍
string构造
前面提到了我们学习string类需要结合文档来学习,这里我们在文档中查询string类的构造。
文档下面也对这几个构造函数有解释>
Google翻译>
string();
创建一个空的字符串,长度为零个字符。
eg:
int main()
{
//构造一个空的字符串
string s1;
cout << s1 << endl;
return 0;
}
可以看到打印出来是空白的。
string (const char* s);
构造一个s字符串的拷贝。
eg:
int main()
{
//构造一个“hello C++”的字符串
string s0("hello C++");
cout << s0 << endl;
return 0;
}
string (const string& str);
拷贝构造函数,构造一个str字符串的副本。
eg:
int main()
{
//构造一个“hello C++”的字符串
string s0("hello C++");
//通过s0字符串拷贝构造出s2
string s2(s0);
cout << s0 << endl;
cout << s2 << endl;
return 0;
}
string (const string& str, size_t pos, size_t len = npos);
复制从字符位置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翻译>
string (const char* s, size_t n);
这个构造函数的作用是从s字符串中拷贝前n个字符赋给构造的对象。
eg:
int main()
{
string s5("C++ is very Good!", 12);
cout << s5 << endl;
return 0;
}
string (size_t n, char c);
用字符c的n 个连续拷贝填充字符串。
eg:
int main()
{
string s6(10, 'c');
cout << s6 << endl;
return 0;
}
template < class InputIterator >string (InputIterator first, InputIterator last);
范围构造函数
以相同的顺序复制[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;
}
string类对象的容量操作
size
size的功能返回字符串的有效长度,以字节为单位。这是符合字符串内容的实际字节数,不一定等于它的存储容量。
eg:
int main()
{
string str("hello C++!");
cout << str.size() << endl;
return 0;
}
length
length的功能与size的功能相同,都是返回字符串的有效长度。
eg:
int main()
{
string str("hello C++!");
cout << str.size() << endl;
cout << str.length() << endl;
return 0;
}
capacity
返回当前为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返回的是字符串可达到的最大长度)
empty
测试字符串是否为空
返回字符串是否为空(即它的长度是否为0)
如果字符串长度为0则为true,否则为false。
clear
清除字符串
擦除string 的内容,它变成一个空字符串(长度为0个字符)。
reserve
reserve(size_t n=0):为string预留空间,不改变有效元素个数,当reserve的参数小于
string的底层空间总大小时,reserver不会改变容量大小。
resize
resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。
注意:
- size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一致,一般情况下基本都是用size()。
- clear()只是将string中有效字符清空,不改变底层空间大小。
string类对象的访问及遍历操作
operator[]
他的功能是可以返回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修饰的对象当然也不能被改变。
iterator迭代器
我们来看这样一段代码>
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;
}
reverse_iterator反向迭代器
如果想要倒着打印字符串也可以通过反向迭代器来进行操作>
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
对象中第一个字符之前的位置。
了解了上面两个函数我们再来运行上面代码看结果>
范围for
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底层其实就是调用使用迭代器来完成>
通过反汇编代码我们可以看到在我们使用范围for的时候调用了两个迭代器函数,分别是begin()和end()。
string类对象的修改操作
push_back()
将字符附加到字符串
将字符c附加到字符串的末尾,将其长度增加一。
int main()
{
string str("hello C+");
cout << str << endl;
str.push_back('+');
cout << str << endl;
return 0;
}
append()
附加到字符串
通过在当前值的末尾附加额外的字符来 扩展字符串:
append也重载了许多个函数。我们这里介绍几个比较常用的。
string& append (const string& str);
这个函数的功能是追加str的拷贝。
eg:
int main()
{
string str1("hello ");
string str2("C++");
str1.append(str2);
cout << str1 << endl;
return 0;
}
string& append (const string& str, size_t subpos, size_t sublen);
追加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;
}
从str2的第0个位置开始追加追加两个字符。
string& append (const char* s);
在字符串后面追加一个字符串。
eg:
int main()
{
string str1("hello ");
str1.append("C++");
cout << str1 << endl;
return 0;
}
string& append (const char* s, size_t n);
在s指向的字符数组中追加前n个字符的副本。
eg:
int main()
{
string str1("hello ");
str1.append("C++! hello",3);
cout << str1 << endl;
return 0;
}
在str后面追加"C++ hello"
字符串中的前三个字符。
string& append (size_t n, char c);
填充
追加字符c的n 个连续拷贝。
eg:
int main()
{
string str1("hello C");
str1.append(2,'+');
cout << str1 << endl;
return 0;
}
operator +=
这个重载也是字符串追加的功能。可以追加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()
c_str()
是 string
类的成员函数,用于返回一个指向 string
对象中第一个字符的指针。该指针指向的字符数组以空字符结尾,可以用于与 C 风格字符串进行交互。以下是 c_str()
的用法和示例:
#include <iostream>
#include <string>
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 + npos
find()
是 string
类的成员函数,用于在字符串中查找指定的子串,并返回其位置。npos
是 string
类的静态成员变量,表示未找到指定子串时的返回值。以下是 find()
和 npos
的用法和示例:
#include <iostream>
#include <string>
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()
函数是否查找到了指定子串。
注意:
- 在string尾部追加字符时,
s.push_back(c) / s.append(1, c) / s += 'c'
三种的实现方式差不多,一般
情况下string类的+=
操作用的比较多,+=
操作不仅可以连接单个字符,还可以连接字符串。 - 对
string
操作时,如果能够大概预估到放多少字符,可以先通过reserve
把空间预留好。
string类非成员函数
我们上面一直在使用cout<<str<<endl;在对字符串进行打印输出,string不是内置类型,为什么还可以使用输出<<打印呢?
这是因为string类外面重载了operator<<(string)和operator>>(string)。
将字符串插入流
将符合str
值的字符序列插入到os
中。
此函数重载operator<<
使其行为如ostream::operator<<
中所述,适用于 C 字符串,也适用于字符串对象。
从流中提取字符串
从输入流is
中提取一个字符串,将序列存储在str
中,它被覆盖( str的先前值被替换)。
此函数重载operator>>
使其行为如istream::operator>>
中描述的用于 c 字符串,但应用于字符串对象。
每个提取的字符都附加到字符串,就好像它的成员push_back
被调用一样。
getline
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)字符串比较函数
relational operators
是 string
类的比较运算符,用于比较两个 string
类型的对象。
以下是 relational operators
的用法和示例:
==
运算符:判断两个string
对象是否相等,如果相等则返回true
,否则返回false
。
#include <iostream>
#include <string>
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 <iostream>
#include <string>
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 <iostream>
#include <string>
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 <iostream>
#include <string>
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 <iostream>
#include <string>
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 <iostream>
#include <string>
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仓库!