1. 为什么学习string类?
1、C语言中的字符串 C语言中,字符串是以’\0’结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。
2、在OJ中, 有关字符串的题目基本以string类的形式出现,而且在常规工作中,为了简单、方便、快捷,基本都使用string类,很少有人去使用C库中的字符串操作函数。
2. 标准库中的string类
2.1 string类的常用接口说明
2.1.1 string类对象的常见构造
(constructor)函数名称 | 功能说明 |
---|
string() (重点) | 构造空的string类对象,即空字符串 | string(const char* s) (重点) | 用C-string来构造string类对象 | string(size_t n, char c) | string类对象中包含n个字符c | string(const string&s) (重点) | 拷贝构造函数 |
int main()
{
string s1;
string s2("hello world");
string s3(s2);
string s5(s2, 2);
string s6(s2, 2, 100);
string s7("hello world", 3);
string s8(10, '!');
return 0;
}
2.1.2 string类对象的容量操作
函数名称 | 功能说明 |
---|
size(重点) | 返回字符串有效字符长度 | length | 返回字符串有效字符长度 | capacity | 返回空间总大小 | empty (重点) | 检测字符串释放为空串,是返回true,否则返回false | clear (重点) | 清空有效字符 | reserve (重点) | 为字符串预留空间 | resize (重点) | 将有效字符的个数该成n个,多出的空间用字符c填充 |
size、length、max_size、capacity
void test1()
{
string s1;
cin >> s1;
cout << s1.size() << endl;
cout << s1.length() << endl;
cout << s1.max_size() << endl;
cout << s1 << endl;
cout << s1.capacity() << endl;
s1.clear();
cout << s1 << endl;
cout << s1.capacity() << endl;
return ;
}
resize
void resize (size_t n);
void resize (size_t n, char c);
resize规则: ?1、当n大于对象当前的size时,将size扩大到n,扩大的容量用字符c填充,若c未给出,则默认为’\0’。 ?2、当n小于当前对象的size时,将size缩小到n。 ?3、若n大于当前对象capacity,则会先扩容后填充。
void test2()
{
string s1;
s1.resize(10, 'x');
s1.resize(16);
s1.resize(5);
cout << s1.capacity() << endl;
cout << s1.size() << endl;
}
reserve
void reserve (size_t n = 0);
reserve 规则: ?1、当n大于对象当前的capacity时,将capacity扩大到n或大于n。 ?2、当n小于对象当前的capacity时,什么也不做。
void test3()
{
string s("string");
cout << s << endl;
cout << s.size() << endl;
cout << s.capacity() << endl;
s.reserve(18);
cout << s << endl;
cout << s.size() << endl;
cout << s.capacity() << endl;
s.reserve(2);
cout << s << endl;
cout << s.size() << endl;
cout << s.capacity() << endl;
}
reserve的增容情况
void TestPushBackReserve()
{
string s;
size_t sz = s.capacity();
cout << "making s grow:\n";
for (int i = 0; i < 100; ++i)
{
s.push_back('c');
if (sz != s.capacity())
{
sz = s.capacity();
cout << "capacity changed: " << sz << '\n';
}
}
注意:vs下是按照1.5倍增长的,根据环境有关。
reserve提高效率
void TestPushBack()
{
string s;
s.reserve(100);
size_t sz = s.capacity();
cout << "making s grow:\n";
for (int i = 0; i < 100; ++i)
{
s.push_back('c');
if (sz != s.capacity())
{
sz = s.capacity();
cout << "capacity changed: " << sz << '\n';
}
}
小结:
- 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在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大 小,如果是将元素个数减少,底层空间总大小不变。 - reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于
string的底层空间总大小时,reserver不会改变容量大小。
2.1.3 string类对象的访问及遍历操作
函数名称 | 功能说明 |
---|
operator[] (重点) | 返回pos位置的字符,const string类对象调用 | begin+ end begin | 获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器 | rbegin + rend | begin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器 | 范围for | C++11支持更简洁的范围for的新遍历方式 |
operator[]与at()
char& operator[] (size_t pos);
const char& operator[] (size_t pos) const;
char& at (size_t pos);
const char& at (size_t pos) const;
char& operator[] (size_t pos)
{
assert(pos < _size);
return _str[pos];
}
用法:
int main()
{
string s1("hello world");
cout << s1 << endl;
for (size_t i = 0; i < s1.size(); ++i)
{
cout << s1.operator[](i) << " ";
cout << s1[i] << " ";
}
cout << endl;
for (size_t i = 0; i < s1.size(); ++i)
{
s1[i] += 1;
}
cout << endl;
cout << s1 << endl;
for (size_t i = 0; i < s1.size(); ++i)
{
s1.at(i) -= 1;
}
cout << endl;
cout << s1 << endl;
try
{
s1.at(100);
}
catch (exception& e)
{
cout << e.what() << endl;
}
return 0;
}
operator[]与at()的区别 operator对非法访问进行断言处理,at()会抛异常。 注意:返回值是引用,我们可以对返回值修改读取。
begin与end函数
begin函数:返回一个指向字符串第一个字符的迭代器。
函数原型:
?iterator begin();
?const_iterator begin() const;
end函数:返回一个指向字符串结束字符的迭代器,即’\0’。 函数原型:
?iterator end();
?const_iterator end() const;
使用迭代器访问对象中的元素
void test_string1()
{
string s1("hello world");
string::iterator it = s1.begin();
while (it != s1.end())
{
*it -= 1;
++it;
}
cout << endl;
auto it = s1.rbegin();
while (it != s1.rend())
{
cout << *it << " ";
++it;
}
cout << endl;
}
使用范围for访问对象中的元素
void test_string1()
{
string s1("hello world");
for (auto& e : s1)
{
e -= 1;
}
for (auto e : s1)
{
cout << e << " ";
}
cout << endl;
}
注意:如果要修改数据,必须是引用,不然只是对临时变量进行修改。 范围for的底层其实就是迭代器访问。
const迭代器用法:
void func(const string& s)
{
auto it = s.begin();
while (it != s.end())
{
cout << *it << " ";
++it;
}
cout << endl;
string::const_reverse_iterator rit = s.rbegin();
while (rit != s.rend())
{
cout << *rit << " ";
++rit;
}
cout << endl;
}
cbegin(),cend(),crbegin(),crend(),这些函数返回const正/反迭代器。目的是为了提高可读性。
2.1.4 string类对象的修改操作
函数名称 | 功能说明 |
---|
push_back | 在字符串后尾插字符c | append | 在字符串后追加一个字符串 | operator+= (重点) | 在字符串后追加字符串str | c_str(重点) | 返回C格式字符串 | find + npos(重点) | 从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置 | rfind | 从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置 | substr | 在str中从pos位置开始,截取n个字符,然后将其返回 |
c_str()
const char* c_str() const;
返回一个指向数组的指针,该数组包含一个以空字符结尾的字符序列(即一个 C 字符串),表示字符串对象的当前值。此数组包含构成字符串对象值的相同字符序列,以及末尾的附加终止空字符 ( ‘\0’ )。
void test()
{
string file("test.txt");
FILE* fout = fopen(file.c_str(), "w");
}
substr()
string substr (size_t pos = 0, size_t len = npos) const;
生成字串,返回一个新建的细绳对象,其值初始化为此对象的子字符串的副本。 子字符串是从字符位置pos开始并跨越len个字符(或直到字符串末尾,以先到者为准)的对象部分。
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1("BBQ321");
string s2;
s2 = s1.substr(2, 4);
cout << s2 << endl;
return 0;
}
find函数 & rfind 函数 & npos
size_t find (const string& str, size_t pos = 0) const; //从POS位置开始查找str
size_t find (const char* s, size_t pos = 0) const;//找s字符串从POS位置开始找。
size_t find (const char* s, size_t pos, size_t n) const;//查找s的前n个字符在string pos开始位置找
size_t find (char c, size_t pos = 0) const;//查找c字符,从pos位置开始
返回值: 第一个匹配的第一个字符的位置。 如果未找到匹配项,则该函数返回string::npos。 静态常量 size_t npos = -1; size_t 的最大值npos是一个静态成员常量值,对于size_t类型的元素具有最大可能值。 此值在用作string的成员函数中的len(或sublen)参数的值时,表示“直到字符串的结尾”。 作为返回值,它通常用于表示不匹配。 此常量定义为-1值,因为size_t是无符号整数类型,所以它是该类型的最大可能表示值。 例子代码:
void test_string4()
{
size_t pos = file.find('.');
if (pos != string::npos)
{
string suffix = file.substr(pos, file.size() - pos);
string suffix = file.substr(pos);
cout << suffix << endl;
}
========================================================================================
string url("http://www.cplusplus.com/reference/string/string/find/");
size_t pos1 = url.find(':');
string protocol = url.substr(0, pos1 - 0);
cout << protocol << endl;
size_t pos2 = url.find('/', pos1+3);
string domain = url.substr(pos1 + 3, pos2 - (pos1 + 3));
cout << domain << endl;
string uri = url.substr(pos2 + 1);
cout << uri << endl;
}
insert()
string& insert (size_t pos, const string& str);//在POS位置插入
string& insert (size_t pos, const char* s);
iterator insert (iterator p, char c);
void test6()
{
string s("abcd");
string tmp("BBQ");
s.insert(0,2,'g');
s.insert(s.begin(), 'h');
s.insert(0, "test");
s.insert(0, tmp);
cout << s;
}
注意: 避免中间位置插入,因为要挪动数据,效率低,o(N),尽量少用。
erase
string& erase (size_t pos = 0, size_t len = npos);//从POS位置开始删len长度,默认删到末尾
iterator erase (iterator p);//删除迭代器指向的内容
iterator erase (iterator first, iterator last);//删除迭代器区间的内容
void test_string6()
{
string s("hello world");
s.erase(0, 1);
s.erase(s.size() - 1, 1);
cout << s << endl;
s.erase(3);
cout << s << endl;
}
注意:迭代器删除不常用。
append & operator+=
void Teststring()
{
string str;
str.push_back(' ');
str.append("hello");
str += 'B';
str += "BQ";
cout<<str<<endl;
}
小结:
- 在string尾部追加字符时,s.push_back( c ) / s.append(1, c) / s += 'c’三种的实现方式差不多,一般
情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可以连接字符串。 - 对string操作时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留好。
2.1.5 string类非成员函数
函数 | 功能说明 |
---|
operator+ | 尽量少用,因为传值返回,导致深拷贝效率低 | operator>> | (重点) 输入运算符重载 | operator<< | (重点) 输出运算符重载 | getline (重点) | 获取一行字符串 | relational operators | (重点) 大小比较(关系运算符) |
getline()
istream& getline (istream& is, string& str, char delim);
istream& getline (istream& is, string& str);
从is 中 提取字符并将它们存储到str中,直到找到分隔符delim(或换行符’\n’,对于(2))。
getline原理:
int main()
{
string s;
char ch;
cin >> ch;
while (ch != '\n')
{
s += ch;
cin >> ch;
}
cout <<s<<endl;
return 0;
}
getline用法:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1,s2;
getline(cin, s1);
cout << s1 << endl;
cin>>s1;
cout<< s2 <<endl;
return 0;
}
用法二、
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s;
getline(cin, s, 'Q');
cout << s << endl;
return 0;
}
|