目录
一.为什么学习string类
(1) C语言中的字符串
(2)标准库里面的string类
二. string类的常用接口说明
(1)string类对象的常见构造
(2)string类对象的容量操作
1.size(),length().
2. capacity()
3.empty()
4.clear()
5.reserve()
6.resize()
(3)string类对象的访问及遍历操作
1.operator[ pos ] ,at(size_t pos)
2.back(),front()
(4)string类的迭代器
1.begin(),end()
2.rbegin() ,rend()
3.范围for
(5) string类对象的修改操作
1.push_bank( )
2.append()
3.operator +=
4.insert ()
5.erase()
6.swap()
(6)字符串相关算法
1.c_str()
2.find()
3.substr()
(7) 非成员函数重载
1.getline()
一.为什么学习string类
(1) C语言中的字符串
C语言中,字符串是以'\0'结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。
在OJ中,有关字符串的题目基本以string类的形式出现,而且在常规工作中,为了简单、方便、快捷,基本都使用string类,很少有人去使用C库中的字符串操作函数。
(2)标准库里面的string类
string类文档
- 字符串是表示字符序列的类
- 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作单字节字符字符串的设计特性。
- string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型(关于模板的更多信息,请参阅basic_string)。
- string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_traits和allocator作为basic_string的默认参数(根于更多的模板信息请参考basic_string)。
- 注意,这个类独立于所使用的编码来处理字节:如果用来处理多字节或变长字符(如UTF-8)的序列,这个类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。
总结:
1. string是表示字符串的字符串类
2. 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。
3. string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator>string;
4. 不能操作多字节或者变长字符的序列在使用string类时,必须包含#include头文件以及using namespace std;
二. string类的常用接口说明
(1)string类对象的常见构造
string() (重点) | 创建空的string类型对象,即空字符串 |
string(const char* s) (重点) | 用C-string来构造string类对象 |
string(size_t n, char c) | string类对象中包含n个字符c |
string(const string&s) (重点) | 拷贝构造函数 |
#include<iostream>
#include<string>
using namespace std;
int main()
{
//创建空字符串
string str1;
cout << str1 << endl;
//使用字符串构造对象
string str2("hello world");
cout << str2 << endl;
//使用n个字符构造对象
string str3(15, '*');
cout << str3 << endl;
//拷贝构造函数
string str4(str2);
cout << str4 << endl;
return 0;
}
(2)string类对象的容量操作
课件代码/C++课件V6/string的接口测试及使用/TestString.cpp · will/C++上课 - Gitee.com
1.size(),length().
size()和lenth都是返回字符串有效长度的。
注意:我们一般表示字符串的长度时使用length,引入size()的原因是为了与其他容器的接口保持一致,一般情况下基本都是用size()。
int main()
{
string str("hello world");
cout << str.size() << endl;
cout << str.length() << endl;
return 0;
}
2. capacity()
capacity()是返回字符串对象的总空间大小的,因为string自己本身是可以扩容的。
int main()
{
string str("hello world");
cout << str.size() << endl;
cout << str.length() << endl;
cout << str.capacity() << endl;
return 0;
}
3.empty()
检测字符串释放为空串,是返回true,否则返回false。
int main()
{
string str("hello world");
string str1;
cout << str.empty() << endl;
cout << str1.empty() << endl;
return 0;
}
4.clear()
清楚有效字符。
注意:clear()只是将string中有效字符清空,不改变底层空间大小。
int main()
{
string str("hello world hello world hello ");
cout << "clear前:" << str <<" 容量:" << str.capacity() << endl;
str.clear();
cout << "clear前:" << str << "容量:" << str.capacity() << endl;
return 0;
}
5.reserve()
为字符串预留空间,我们直到在一些情况下如果我们提前知道了字符串的大小,就可以提前为字符串开好空间,避免后面的扩容带来的效率上的消耗。
注意:实际上的申请后的空间会比我们申请的空间大一些,这也是一种保护机制。但是我们如果我们预留的空间比原本的空间还要小时,此时函数会进行优化,个根据我们存储的串的长度经行合适的改表。
6.resize()
resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。如果 n 小于字符串长度,就删去字符串保留前 n 个。
int main()
{
string str("abcde");
cout << str << endl;
//扩容+初始化
str.resize(str.size() + 5, '#');
cout << str << endl;
//减小长度
str.resize(4);
cout << str << endl;
return 0;
}
(3)string类对象的访问及遍历操作
1.operator[ pos ] ,at(size_t pos)
operator [ ] 都是返回pos位置的字符,const string类对象调用。operater[ ]是对[ ]的运算符重载,就像数组一样的访问。
注意:两者的区别在,处理越界方面,operator[ ]使用断言处理,而 at(pos) 是抛出一个异常。
int main()
{
string str("hello");
for (int i = 0; i < str.size(); i++)
{
cout << str[i] << " ";
}
cout << endl;
for (int i = 0; i < str.size(); i++)
{
cout << str.at(i) << " ";
}
return 0;
}
2.back(),front()
back():返回最后一字符,front():返回第一个字符。
(4)string类的迭代器
1.begin(),end()
begin():返回一个迭代器,该迭代器指向字符串的开始字符。
end():返回一个迭代器,该迭代器指向字符串的结束字符。
迭代器,也是一个用来遍历对象的一个东西,它有自己的类型 iterator ,针对begin(),和end(),也都有自己的const版本,也就是仅可读迭代器。string的迭代器的使用和指针差不多。
当前可以这么理解,随着我们STL的不断学习,会对迭代器有更加深刻的理解。
int main()
{
string str("helloworld");
//非const迭代器,可读,可写
for (string::iterator it = str.begin();it!=str.end();it++)
{
*it = '*';
}
for (string::iterator it = str.begin(); it != str.end(); it++)
{
cout << *it << " ";
}
cout << endl;
//const 迭代器 仅可读
const string str1("helloworld");
for (string::const_iterator it = str1.begin(); it != str1.end(); it++)
{
cout << *it << " ";
}
return 0;
}
2.rbegin() ,rend()
rbegin():返回反向迭代器以反向开始,返回一个反向迭代器,指向字符串的最后一个字符(即其反向开头),反向迭代器向后迭代:增加迭代器会将它们移向字符串的开头。
rend():返回一个反向迭代器,该迭代器指向字符串的第一个字符之前的理论元素(被视为其反向结尾)。
反向迭代器的类型是,reverse_iterator,同时都提供了,const版本。
int main()
{
string str("helloworld");
for (string::reverse_iterator it = str.rbegin(); it != str.rend(); it++)
{
cout << *it << " ";
}
return 0;
}
对于迭代器的类型写起来非常的不方便,我们可以使用auto来自动识别。
3.范围for
范围for是将str对象的字符一个一个取出来,放到 c 中。其底层还是使用的迭代器。
int main()
{
string str("helloworld");
for (auto c : str)
{
cout << c << " ";
}
return 0;
}
(5) string类对象的修改操作
1.push_bank( )
在字符串后尾插字符c。
int main()
{
string str("hello");
cout << str << endl;
str.push_back('w');
str.push_back('o');
str.push_back('r');
str.push_back('l');
str.push_back('d');
cout << str<<endl;
return 0;
}
2.append()
在字符串后追加一个字符串,同时也提供了多个版本:
string& append (const string& str):追加一个字符串 str 。 string& append (const string& str, size_t subpos, size_t sublen):追加字符串str的pos位置后的sublen个字符。 string& append (const char* s):追加一个C字符串,(即以‘/0’结束的字符串)。 string& append (const char* s, size_t n):追加C字符串s的后n个字符。 string& append (size_t n, char c):追加 n 个字符 c
int main()
{
string str;
string str1;
cout << str << endl;
string str2 = "Writing ";
//str1追加str2的第四个位置的后三个字符
str1.append(str2, 4,3);
//str追加str2
str.append(str2);
cout << str << endl;
cout << str1 << endl;
//str2z追加一个C字符串
str2.append("work");
cout << str2 << endl;
//str2追加一个c字符串的前两个字符
str2.append("hello", 2);
cout << str2 << endl;
//str2追加10个‘#’
str2.append(10, '#');
cout << str2 << endl;
return 0;
}
3.operator +=
对于上述的push_bank和append其实在string类中使用并不多,我们一般常用operator+=来追加字符串或者追加字符。
例:
int main()
{
string str1("hello");
string str2("world");
str1 += str2;
cout << str1 << endl;
return 0;
}
4.insert ()
字符串插入函数,可以支持在某一位,或者迭代器位置插入固定个数的字符,和字符串。
例:
int main()
{
string str1("hello");
string str2("world");
int pos = 0;
//1.在pos位置插入一个 string
str2.insert(pos, str1);
cout << str2 << endl;
string str3("bcde");
int size = 5;
//2.在pos位置插入size个字符a
str3.insert(pos, size, 'a');
cout << str3 << endl;
string str4("bcde");
//3.插入位置还可以是迭代器
str4.insert(str4.begin(), 10, 'A');
cout << str4 << endl;
string str5("world");
const char* cstr = "hello ";
//3.在pos位置插入一个 C风格字符串。
str4.insert(pos,cstr);
cout << str5 << endl;
return 0;
}
注意:insert,在插入时,特别是在字符串的前部插入是,会有字符的移动消耗。相当于顺序表的头插。
5.erase()
rases字符串的一部分,缩短其长度。
例:
int main()
{
int pos = 5;
int size = 3;
string str1("hello C++");
string str2 = str1;
cout << "erase前str1:" << str1 << endl;
cout << "erase前str2:" << str2 << endl;
//删除pos位置包括pos位置的后size个字符,删除位置也可以是一段迭代器区间
str1.erase(pos, size);
str2.erase(str2.begin()+5,str2.begin()+5+3);
cout << "erase后str1:" << str1 << endl;
cout << "erase后str2:" << str2 << endl;
return 0;
}
注意:
- 如果给出的删除个数大于字符串后面的字符数,就会将后面字符全部删除。
- 如果不给出删除位置设定的缺省参数从0开始删除。
- 如果不给出删除个数,缺省参数设定的删除个数是 size_t npos = -1,约42亿。
- 删除以后字符串的长度(length)会变,但是容量(capacity)不会变。
6.swap()
swap交换函数,非功能上非常简单,就不多介绍了。但是在 std 里面也有一个swap,我们两个swap来对比一下。
std::swap()底层是一个由借助函数模板实现。不仅仅可以对string类型,对任何可以修改的类型都可以实现交换。
原理类似:
template<class T>
void Swap(T& e1,T& e2)
{
T tmp = e1;
e1 = e2;
e2 = tmp;
}
string::swap()底层是交换string里面的字符串指针来实现的,总体效率要比std::swap()高得多。
(6)字符串相关算法
1.c_str()
返回一个C风格字符串。
2.find()
在字符串中搜索由其参数指定的序列的第一个匹配项。
指定pos时,搜索仅包括位置pos处或之后的字符,忽略任何可能出现的位置pos之前的字符。
例如:
int main()
{
string str = "this is a string";
//从1位置开始查找字符a,并返回位置。
size_t pos = str.find('a', 1);
cout << "a 在" << pos << "位置" << endl;
return 0;
}
注意:
- 如果未给出查找位置,就从缺省位置0开始查找。
- 如果没有找到会返回size_t npos = -1。
3.substr()
返回一个新构造的字符串对象,该对象的值初始化为此对象的子字符串的副本。
子字符串是对象的一部分,从字符位置pos开始,跨越len个字符(或直到字符串结束,以先到者为准)。
例:
int main()
{
int pos = 0;
int length = 4;
string str = "this is a string";
//返回一个从pos位置开始,跨越长度为4的子串。
string Substr = str.substr(0, 4);
cout << "Substr:" << Substr << endl;
return 0;
}
注意:
如果没给出起始位置,缺省参数设置为0。
如果没给出跨越长度,缺省参数设置为size_t npos = -1。
(7) 非成员函数重载
1.getline()
从is中提取字符并将其存储到str中,直到找到定界字符delim。没有delim时默认是 ' \n '为定界符 。
例:
int main()
{
string str;
cout << "输入:";
std::getline(cin, str,'a');
cout << "str:" << str << endl;
return 0;
}