C++中的string类使用,看我这一篇就够了!
由于cpp中的string类的函数数量很多,用法繁杂,我替大家进行了总结
1. 标准库中的string类
1.1 string类介绍
https://cplusplus.com/reference/string/string/?kw=string
- 字符串是表示字符序列的类
- 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作单字节字符字符串的设计特性。
- string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型(关于模板的更多信息,请参阅basic_string)。
- string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_traits和allocator作为basic_string的默认参数(根于更多的模板信息请参考basic_string)。
- 注意,这个类独立于所使用的编码来处理字节:如果用来处理多字节或变长字符(如UTF-8)的序列,这个类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。
1.2 对上述string类的总结
-
string是表示字符串的字符串类
-
该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。
-
string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator>string;
-
不能操作多字节或者变长字符的序列。
在使用string类时,必须包含#include头文件以及using namespace std;
2.string类常用函数用法
2.1 string类对象的常用构造
#include <iostream>
#include <string>
using namespace std;
void test1()
{
//0.构造空string对象,即空字符串
string str;
//1.生成"123456789"(方法一) 使用同一个类的对象来初始化另一个同类型的对象
string str1 = "123456789";
//2.生成"123456789"(方法二) string (const string& str); str1是"123456789"的复制品
string str2("123456789");
//3.string (const string& str, size_t pos, size_t len = npos); 将字符串str中从下标pos开始、长度为len的部分作为字符串初值
string str3("12345", 0, 3);
//4.string (cstr, size_t n); 以C_string类型cstr的前n个字符串作为字符串s的初值
string str4("012345",5);
//5.string (size_t n, char c); 生成n个c字符的字符串
string str5(5, '1');
//6.string s(str, x):将字符串str中从下标x开始到字符串结束的位置作为字符串初值
string str6(str2, 2);
cout << str1 << endl;//结果为123456789
cout << str2 << endl;//结果为123456789
cout << str3 << endl;//结果为123
cout << str4 << endl;//结果为01234
cout << str5 << endl;//结果为11111
cout << str6 << endl;//结果为3456789
}
int main()
{
test1();
return 0;
}
注意:4与6的区别为4是c风格的字符串(即以空字符\0结尾的字符数组)
运行结果如图
2.2 string类对象的容器操作
void test2()
{
string s("123456");
cout << "size=" << s.size() << endl;//返回字符串有效字符长度
cout << "length=" << s.length() << endl;//返回字符串有效字符长度
cout << "capacity=" << s.capacity() << endl;//返回总空间大小
cout << "empty=" << s.empty() << endl;//检查字符串是否是空串,是返回true,否则返回false
cout << "---------------------------------------" << endl;
s.resize(10);
cout << "size=" << s.size() << endl;//返回字符串有效字符长度
cout << "capacity=" << s.capacity() << endl;//返回总空间大小
s.resize(10,'a');
cout << "size=" << s.size() << endl;//返回字符串有效字符长度
cout << "capacity=" << s.capacity() << endl;//返回总空间大小
cout << s << endl;
s.resize(3);
cout << s << endl;
cout << "size=" << s.size() << endl;//返回字符串有效字符长度
cout << "capacity=" << s.capacity() << endl;//返回总空间大小
cout << "---------------------------------------" << endl;
string t("123456");
cout << t << endl;
cout << "size=" << t.size() << endl;//返回字符串有效字符长度
cout << "capacity=" << t.capacity() << endl;//返回总空间大小
t.reserve(20);
cout << "size=" << t.size() << endl;//返回字符串有效字符长度
cout << "capacity=" << t.capacity() << endl;//返回总空间大小
t.reserve(3);
cout << "size=" << t.size() << endl;//返回字符串有效字符长度
cout << "capacity=" << t.capacity() << endl;//返回总空间大小
cout << "--------------------------------------" << endl;
s.clear();
cout << "clear=" << s << endl;//清空有效字符串
}
你看懂了吗? 结果如图
string容量相关方法使用注意:
-
size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一
致,一般情况下基本都是用size()。 -
clear()只是将string中有效字符清空,不改变底层空间大小。
-
resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。
-
注意:resize()函数会影响string对象的size(),当容量不够需要扩容时则同时会影响到capacity()
a.如果新的大小比原来的大小大,那么多出来的元素会用默认值或者指定值进行初始化(刚刚提到)。
b.如果新的大小比原来的大小小,那么多余的元素会被删除。 -
reserve(size_t res_arg=0):为string预留空间:
reserve()函数不会影响string对象的size(),但会影响它的capacity()。
a.如果新的容量比原来的容量大,那么string对象会分配更多的内存空间,以避免后续插入元素时发生内存重新分配。
b.如果新的容量比原来的容量小,那么reserve()函数不会做任何事情。
2.3 string类对象的访问以及遍历操作
void test3()
{
string s1("abcdef");
// 方法一: 下标法
for (int i = 0; i < s1.size(); i++)
{
cout << s1[i];
}
cout << endl;
// 方法二:正向迭代器
string::iterator it ;
for (it = s1.begin(); it < s1.end(); it++)
{
cout << *it;
}
cout << endl;
//方法二的简化版用auto进行自动类型推导
for (auto it = s1.begin(); it != s1.end(); it++)
{
cout << *it;
}
cout << endl;
// 方法三:反向迭代器
string::reverse_iterator rit ;
for (rit = s1.rbegin(); rit < s1.rend(); rit++)
{
cout << *rit;
}
cout << endl;
//方法三的简化版用auto进行自动类型推导
for (auto rit = s1.rbegin(); rit != s1.rend(); rit++)
{
cout << *rit;
}
cout << endl;
//方法四: 范围for
for (auto e : s1)
{
cout << e;
}
cout << endl;
}
结果如图
注意:
1.利用迭代器遍历string是可以使用auto自动类型推导,详细讲解见我的博客https://blog.csdn.net/2302_80295520/article/details/141598383?spm=1001.2014.3001.5501
2.范围for的底层其实也是和迭代器相同,只是使用起来更加方便
2.4 string类对象的修改操作-push_back()&insert()&append()&operator+=
void test4()
{
string s1;
// 在字符串后尾插一个字符
s1.push_back('a');
s1.push_back('b');
s1.push_back('c');
cout << "s1:" << s1 << endl; // s1:abc
// insert(pos,char):在制定的位置pos前插入字符char
s1.insert(s1.begin(), '1');
cout << "s1:" << s1 << endl; // s1:1abc
cout << "----------------------------------" << endl;
//在字符串后追加一个字符串
//方法1:使用append操作:
string s2("abc");
cout << s2 << endl;
s2.append("111");
cout << s2 << endl;
//方法2:+ 操作符
string s3("abc");
cout << s3 << endl;
string t("111");
cout << s3 + t << endl;
cout << "----------------------------------" << endl;
}
注意:
- 在string尾部追加字符时,s.push_back© / s.append(1, c) / s += 'c’三种的实现方式差不多,一般
情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可以连接字符串。 - 对string操作时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留好。
2.5 string类对象的查找操作-find()&rfind() …
void test5()
{
//find
string s("dog bird chicken bird cat");
//字符串查找-----找到后返回首字母在字符串中的下标
// 1. 查找一个字符串,返回该字符串首字符的下标
cout << s.find("chicken") << endl; // 结果是:9
// 2. 从下标为6开始找字符'i',返回找到的第一个i的下标
cout << s.find('i', 6) << endl; // 结果是:11
// 3. 从字符串的末尾开始查找字符串,返回的还是首字母在字符串中的下标
cout << s.rfind("chicken") << endl; // 结果是:9
// 4. 从字符串的末尾开始查找字符
cout << s.rfind('i') << endl; // 结果是:18-------因为是从末尾开始查找,所以返回第一次找到的字符
// 5. 在该字符串中查找第一个属于字符串s的字符
cout << s.find_first_of("13br98") << endl; // 结果是:4---b
// 6. 在该字符串中查找第一个不属于字符串s的字符------先匹配dog,然后bird匹配不到,所以打印4
cout << s.find_first_not_of("hello dog 2006") << endl; // 结果是:4
cout << s.find_first_not_of("dog bird 2006") << endl; // 结果是:9
// 7. 在该字符串最后中查找第一个属于字符串s的字符
cout << s.find_last_of("13r98") << endl; // 结果是:19
// 8. 在该字符串最后中查找第一个不属于字符串s的字符------先匹配t--a---c,然后空格匹配不到,所以打印21
cout << s.find_last_not_of("teac") << endl; // 结果是:21
cout << "----------------------------------" << endl;
}
结果如图
上述string类的查找函数用法
-
size_t find (constchar* s, size_t pos = 0) const;在当前字符串的pos索引位置开始,查找子串s,返回找到的位置索引,-1表示查找不到子串
-
size_t find (charc, size_t pos = 0) const;在当前字符串的pos索引位置开始,查找字符c,返回找到的位置索引,-1表示查找不到字符
-
size_t rfind (constchar* s, size_t pos = npos) const;在当前字符串的pos索引位置开始,反向查找子串s,返回找到的位置索引,-1表示查找不到子串
-
size_t rfind (charc, size_t pos = npos) const;在当前字符串的pos索引位置开始,反向查找字符c,返回找到的位置索引,-1表示查找不到字符
-
size_tfind_first_of (const char* s, size_t pos = 0) const;在当前字符串的pos索引位置开始,查找子串s的字符,返回找到的位置索引,-1表示查找不到字符
-
size_tfind_first_not_of (const char* s, size_t pos = 0) const;在当前字符串的pos索引位置开始,查找第一个不位于子串s的字符,返回找到的位置索引,-1表示查找不到字符
-
size_t find_last_of(const char* s, size_t pos = npos) const;在当前字符串的pos索引位置开始,查找最后一个位于子串s的字符,返回找到的位置索引,-1表示查找不到字符
-
size_tfind_last_not_of (const char* s, size_t pos = npos) const;在当前字符串的pos索引位置开始,查找最后一个不位于子串s的字符,返回找到的位置索引,-1表示查找不到子串
2.6 string类的截取与分割_strtok&substr
void test6()
{
//字符分割函数strtok
//char *strtok(char *str, const char *delim)
//str—要被分解的字符串 ; delim—用作分隔符的字符(可以是一个,也可以是集合)
//该函数返回被分解的第一个子字符串,若无可检索的字符串,则返回空指针
char str[] = "I,am,a,student; hello world!";
const char* split = ",; !";
char* p2 = strtok(str, split);
while (p2 != NULL)
{
cout << p2 << endl;
p2 = strtok(NULL, split);
}
//首次调用 token = strtok(str, s); 时,strtok 会在 str 中查找第一个不包含在 s 中的字符序列,并返回指向该序列的指针(即 token)。然后,它会在找到的序列后面插入一个字符串结束符 '\0' 来结束当前子字符串,并继续搜索下一个子字符串。
//在 while 循环中,使用 token = strtok(NULL, s); 继续从上次停止的地方开始搜索下一个子字符串。strtok 的第一个参数是 NULL 表示继续从上一次分割的位置开始搜索。
//当 strtok 无法找到更多的子字符串时,它会返回 NULL,循环终止。
}
void test7()
{
//substr函数:在str中从pos位置开始,截取n个字符,然后将其返回
string s1("0123456789");
string s2 = s1.substr(2, 5); // 结果:23456-----参数5表示:截取的字符串的长度
cout << s2 << endl;
}
test6()的运行结果
test7()的运行结果
2.7 string的删除:erase()
使用方法:
-
iterator erase(iterator p);删除字符串中p所指的字符
-
iterator erase(iterator first, iterator last);删除字符串中迭代器区间[first,last)上所有字符
-
string& erase(size_t pos = 0, size_t len = npos);删除字符串中从索引位置pos开始的len个字符
-
void clear();删除字符串中所有字符
void test8()
{
string s1 = "123456789";
// s1.erase(s1.begin()+1); // 结果:13456789
// s1.erase(s1.begin()+1,s1.end()-2); // 结果:189
s1.erase(1,6); // 结果:189
string::iterator iter = s1.begin();
while( iter != s1.end() )
{
cout<<*iter;
*iter++;
}
cout<<endl;
}
2.8 string的读取操作-getline
void test9()
{
string name;
cout << "Please, enter your full name: ";
getline(cin, name);//获取一行的字符串
cout << "Hello, " << name << "!\n";
}
结果如图
2.9 string的替换-replace
replace的使用方法:
-
string& replace(size_t pos, size_t n, const char *s);将当前字符串从pos索引开始的n个字符,替换成字符串s
-
string& replace(size_t pos, size_t n, size_t n1, char c); 将当前字符串从pos索引开始的n个字符,替换成n1个字符c
-
string& replace(iterator i1, iterator i2, const char* s);将当前字符串[i1,i2)区间中的字符串替换为字符串s
void test10()
{
string s1("hello,world!");
cout << s1.size() << endl; // 结果:12
s1.replace(s1.size() - 1, 1, 1, '.'); // 结果:hello,world.
// 这里的6表示下标 5表示长度
s1.replace(6, 5, "girl"); // 结果:hello,girl.
// s1.begin(),s1.begin()+5 是左闭右开区间
s1.replace(s1.begin(), s1.begin() + 5, "boy"); // 结果:boy,girl.
cout << s1 << endl;
}
结果如图:
2.10 string 的比较操作-compare
用法:
对于std::string类,它有一个compare成员函数,用于比较两个字符串。这个函数可以比较两个std::string对象、C风格字符串(const char*)或其他字符串类型的对象。compare函数返回一个整数,表示两个字符串在字典序上的比较结果:
如果返回值 < 0,则表示调用对象小于参数对象。
如果返回值 == 0,则表示两者相等。
如果返回值 > 0,则表示调用对象大于参数对象。
void test11()
{
// (A的ASCII码是65,a的ASCII码是97)
// 前面减去后面的ASCII码,>0返回1,<0返回-1,相同返回0
string A("aBcd");
string B("Abcd");
string C("123456");
string D("123dfg");
// "aBcd" 和 "Abcd"比较------ a > A
cout << "A.compare(B):" << A.compare(B) << endl; // 结果:1
// "cd" 和 "Abcd"比较------- c > A
cout << "A.compare(2, 3, B): " << A.compare(2, 3, B) << endl; // 结果:1
// "cd" 和 "cd"比较
cout << "A.compare(2, 3, B, 2, 3): " << A.compare(2, 3, B, 2, 3) << endl; // 结果:0
// 由结果看出来:0表示下标,3表示长度
// "123" 和 "123"比较
cout << "C.compare(0, 3, D, 0, 3): " << C.compare(0, 3, D, 0, 3) << endl; // 结果:0
}
结果如图