目录
一. 什么是STL
二. string类的概述
三. string类的常用接口说明
3.1 字符串对象创建相关接口(构造函数)
3.2 字符串长度和容量相关接口
3.3 字符访问相关接口函数
3.4 字符串删改相关接口函数
3.5 字符查找和子串相关接口函数
3.6 迭代器相关接口函数
3.7 常用的关于string类对象的全局函数
一. 什么是STL
STL,即标准库模板(standard template library),是C++标准库的重要组成部位,是一个包含了很多经常使用的数据结构和算法的软件框架。
C++标准规定的STL有六大组件:容器、迭代器、算法、仿函数、空间配置器和配接器,其中容器就是我们常说的数据结构。
二. string类的概述
string类,是专为用于存储和操作字符串的类,使用STL string容器的接口,可以实现对字符串进行增删查改、计算长度、循环遍历等操作。string类是模板类basic_string以char为模板参数类型的一个实例,其定义为:typedef basic_string<char> string。
那么或许会有人问:字符串的每个字符不都是char类型吗,那么直接将basic_string的参数类型定义为char不就行了吗,为什么还要定义模板再实例化?
其实,我们通常认为的char类型仅仅可以表示英文字母或字符,每个char类型数据的值,通过编码表(通常为ASCII码表)与特定字符对应,由于英文字母仅有26个,加上各种可打印字符不过128个,char类型数据完全足够对英文进行编码。
但是,其他语言就不一样了,中文汉字有几万几十万,char类型数据是无法表示的。因此,为了对汉字进行编码,就引入wchar_t型数据,一个wchar_t型数据占2byte,可以表示60000多个汉字,基本可以涵盖所有常见汉字。
因此,basic_string之所以不直接将成员变量类型设为char而是采用模板,就是为了能够实例化出不同类型的模板参数,以对应不同的编码规则,从而使其适用于全世界的语言。
常见的编码规则
编码:通过值与符号表建立映射关系,从而使值可以转换为各种字符。
- ASCII码 -- 适用于英文。
- unicode -- 全世界文字的通用编码表,又称万国码。它为世界各国语言的每个字符设置了统一且唯一的二进制编码。unicode下面有包括utf-8、utf-16、utf-32这几种细化的编码规则,他们分别表示以无符号的8、16、32个二进制位的数据进行编码。
- gkb -- 为中文量身定制的编码表。
在Windows下,中文采用gkb编码规则,长字节w_char类型数据可用于中文编码。
三. string类的常用接口说明
3.1 字符串对象创建相关接口(构造函数)
- string() -- 创建空字符串对象(仅包含字符串结束标识\0)。
- string(const string& s) -- 拷贝构造,通过一个string类对象创建一个新的string类对象。注意string类的拷贝构造函数完成的是深拷贝,他会为新创建的对象再开辟一块内存空间,用于存储与s对象相同的字符,即:两者表示的字符串内容相同,但并不是存储在同一块空间的字符串。
- string(const char* s) -- 通过一个现有的字符串构造string类对象。
- string(const string& s, size_t pos, size_t len = npos) -- 通过一个string类对象的子字符串构建新的对象,子串的为:从下标pos开始为第一个字符,向后len个字符。缺省参数npos为string类的静态成员变量,值为size_t类型的-1(一个极大的数)。如果pos往后的字符数不足len,那么就用从pos往后的所有字符创建对象。
- string(const char* s, size_t n) -- 用字符串s的前n个字符构建字符串。
演示代码3.1:
int main()
{
string s1; //空字符串构建
string s2("abcdef"); //通过字符串创建对象
string s3(s2); //通过拷贝构造创建新对象
string s4(s2, 1, 3); //通过已有类对象的子串创建新对象 -- "bcd"
string s5("abcdef", 3); //通过字符串的前n个字符创建新对象 -- "abc"
cout << "s1 = " << s1 << endl;
cout << "s2 = " << s2 << endl;
cout << "s3 = " << s3 << endl;
cout << "s4 = " << s4 << endl;
cout << "s5 = " << s5 << endl;
return 0;
}
3.2 字符串长度和容量相关接口
- size -- 获取字符串长度(不包括末尾\0),函数原型为:size_t size() const。
- length -- 获取字符串长度(不包括末尾\0),函数原型为:size_t length() const。
在string类中,size和length没有任何区别。但是,size可以用在树、链表等其他数据结构中计算数据量,而length不行。为了保证STL中各种数据结构的接口名称统一,一般建议使用size。
- capacity -- 获取当前string类对象能够存储的有效字符量,函数原型为:size_t capacity() const。
- reserve -- 将字符串空间扩容到至少一定的值,函数原型为:void reserve(size_t n)。用reserve进行扩容,并一定恰好扩容到n,而是扩容到不小于n。同时,reserve不影响字符串中原来的内容,且不对扩大的那部分空间进行初始化,如果n小于原来的capacity,那么reverse函数不进行任何工作。
- resize -- 使字符串的长度(size\length)变到n,函数原型为:void resize(size_t n)、void resize(size_t n, char ch),这两个函数构成重载。resize函数的工作为:将函数长度扩大到n,在将扩大的部分的内容改为ch,如果不给定ch值,则默认为'\0'。如果n小于原来的长度size,那么就截取前n个内容。
- clear -- 清空字符串的内容,使字符串的长度(size\length)变为0。
- empty -- 判断字符串是否为空。
演示代码3.2:
int main()
{
string s1("abc");
cout << "size = " << s1.size() << endl;
cout << "length = " << s1.length() << endl; //获取s1的长度
cout << "capacity = " << s1.capacity() << endl; //获取s1的容量
cout << endl;
s1.resize(10, 'x');
cout << "s1 = " << s1 << endl; //abcxxxxxxx
cout << "size = " << s1.size() << endl; //10
cout << endl;
s1.reserve(20); //将s1的容量扩大到至少20
cout << "capacity = " << s1.capacity() << endl; // >=20
cout << "size = " << s1.size() << endl; //3
cout << endl;
cout << s1.empty() << endl; //非0
s1.clear(); //清空数据
cout << s1.empty() << endl; //0
cout << "size = " << s1.size() << endl; //0
cout << "capacity = " << s1.capacity() << endl; //31
return 0;
}
3.3 字符访问相关接口函数
- [] -- 下标引用操作符重载:char& operator[](size_t pos)、const char& operator[](size_t pos) const,两个函数构成重载,分别用于访问普通类对象和const类对象。
- at -- 访问指定下标位置处的字符:char& at(size_t pos)、const char& at(size_t pos) const。
重载[]和at都可以达到访问某个下标位置处字符的目的,但是,当出现越界访问时,[]会assert断言出错,at会抛异常,且[]更符合一般的编码习惯,这里推荐使用[]而不是ar。
演示代码3.3:
int main()
{
string s1("abcdef");
const string s2("abcdef");
//使用[],将s1的每个值+1
for (size_t i = 0; i < s1.size(); ++i)
{
s1[i] += 1;
}
cout << s1 << endl; //bcdefg
//[]访问const对象s2的每个字符,只能读不能写
for (size_t i = 0; i < s2.size(); ++i)
{
//s2[i] += 1; //报错
cout << s2[i];
}
cout << endl;
//通过at函数,遍历打印s2的每个字符
for (size_t i = 0; i < s2.size(); ++i)
{
cout << s2.at(i);
}
cout << endl;
return 0;
}
3.4 字符串删改相关接口函数
- operator+= -- 尾插字符或字符串
- string& operator+=(const string& s) -- 通过类对象获取尾插字符串
- string& operator+=(const char* s) -- 直接尾插字符串s
- string& operator+=(char c) -- 尾插字符c
- push_back -- 尾插字符。函数原型为:void push_back(char c)
- append -- 尾插字符串或字符
- string& append(const string& s) -- 通过string类对象获取字符串尾插
- string& append(const char* s) -- 直接尾插字符串
- string& append(const string& s, size_t pos, size_t len = npos) -- 通过获取子字符串尾插
- string& append(const char* s, size_t n) -- 尾插字符串s的前n个字符
- insert -- 在指定位置插入字符或字符串
- string& insert(size_t pos, const string& s) -- 在pos下标处通过string类对象插入字符串
- string& insert(size_t pos, const string& s, size_t subpos, size_t len = npos) -- 在pos位置处插入string对象的一个子字符串
- string& insert(size_t pos, const char* s) -- 在pos下标处插入字符串s
- string& insert(size_t pos, const char* s, size_t n) -- 在pos下标处插入s的前n个字符
- string& insert(size_t pos, size_t n, char c) -- 在pos位置处插入n个c字符
- erase -- 从指定位置开始删除n个字符,函数原型:string& erase(size_t pos = 0, size_t len = npos)
演示代码3.4:
int main()
{
string s1;
s1 += "aaa"; //+=尾插字符串
cout << s1 << endl;
s1 += 'b'; //+=尾插单个字符
cout << s1 << endl;
s1.push_back('c'); //push_back尾插单个字符
cout << s1 << endl;
s1.append("ddd"); //append尾插字符串
cout << s1 << endl;
cout << endl;
string s2("aaaaaaa");
s2.insert(2, "bbbb"); //在下标2的位置插入字符串"bbbb"
cout << s2 << endl;
s2.erase(2, 4); //从下标为2的位置开始删除4个字符
cout << s2 << endl;
return 0;
}
3.5 字符查找和子串相关接口函数
- c_str -- 获取string类对象中的字符串成员(字符串首字符地址)。函数原型为:const char* c_str() const。
- find -- 以指定下标位置pos为起点,从前向后查找特定字符或子字符串,找到了返回字符或子串第一次出现的下标,找不到就返回npos。
- size_t find(const string& s, size_t pos = 0) const -- 查找sting对象s的字符串。
- size_t find(const char* s, size_t pos = 0) const -- 查找字符串s。
- size_t find(const char* s, size_t pos, size_t len) const -- 查找字符串s的一个子串。
- size_t find(char c, size_t pos = 0) const -- 查找字符c。
- refind -- 与find类似,从尾部开始,查找子字符串或字符第一次出现的下标位置。
- substr -- string substr(size_t pos = 0, size_t len = npos) -- 从当前string对象中获取子串,构建一个新的string对象。
演示代码3.5:
int main()
{
string s1("abcdefgh");
cout << s1.c_str() << endl; //获取字符串成员
cout << s1.find("bcd") << endl; //查找子串"bcd" -- 输出1
cout << s1.find("cdeg", 2, 3) << endl; //从下标2处开始查找"cdef"前3个字符构成的子串"cde" -- 输出2
cout << s1.find('e') << endl; //查找字符e -- 输出4
cout << endl;
string s2("abcdefabcdef");
cout << s2.rfind("abc", 9) << endl; //查找下标位置9之前pos最后一次出现的位置 -- 输出6
cout << s2.rfind('e') << endl; //查找字符'e'最后一次出现的位置 -- 输出10
string s3("abcdef");
string s4 = s3.substr(1, 4);
cout << "s4 = " << s4 << endl; //bcde
return 0;
}
3.6 迭代器相关接口函数
迭代器的类型为iterator,是char*的类型重定义名称,其定义语句为:typedef char* iterator -- 对于普通对象的迭代器,typedef const char* const_iterator -- 对于const属性对象的迭代器。除了一般的iterator以外,还有反向迭代器reverse_iterator和const_reverse_iterator。
对iterator类型的变量执行++操作,其指向的位置后移一位、对reverse_iterator类型的成员变量执行++操作,其指向的位置前移一位。
- begin -- 获取指向字符串首元素的指针,函数原型为:iterator begin()和const_iterator begin() const,这两个函数构成重载,分别应用于普通对象和const属性对象。
- end -- 获取指向字符串最后一个元素后面那个位置('\0')的指针,函数原型为:iterator end()和const_iterator end() const。
- rbegin -- 返回指向字符串最后一个字符的指针,函数原型为:reverse_iterator rbegin() 和 const_reverse_iterator rbegin() const。
- rend -- 返回指向字符串第一个字符前一个位置的指针,函数原型为:reverse_iterator rend() 和 const_reverse_iterator rend() const。
演示代码3.6:
int main()
{
string s1("abcde");
//使用正向迭代器遍历s1的每个字符,+1后输出
string::iterator it1 = s1.begin();
while (it1 != s1.end())
{
++(*it1);
cout << *it1; //输出bcdef
++it1;
}
cout << endl;
string s2("abcde");
//使用反向迭代器,反向遍历s2的每个字符并输出
string::reverse_iterator it2 = s2.rbegin();
while (it2 != s2.rend())
{
cout << *it2; //输出edcba
++it2;
}
cout << endl;
return 0;
}
3.7 常用的关于string类对象的全局函数
在C语言阶段,学过函数atoi,功能是将字符串转化为整数。但是,C/C++标准库函数中并没有atoi函数,虽然大部分编译器都已支持atoi函数,但是,依然建议不要使用,这样会降低程序的可移植性,很多老式编译器依然不支持atoi。
- stoi -- 将string类对象转换为int型数据。
- stol -- 将string类对象转换为long int型数据。
- stoul -- 将string类对象转换为unsigned long int型数据。
- stoll -- 将string类对象转换为long ong型数据。
- stoull -- 将string类对象转换为转换为unsigned long long型数据。
- stof -- 将string类对象转换为float型数据。
- stod -- 将string类对象转换为double型数据。
上述函数,会自动排除字符串前部的空格,会通过'+'、'-'字符判断返回值的正负,当遇到非数字字符时,函数会终止执行,返回当前值。
注意:如果string无法转换为数字(首个非空字符不是'+'、'-'或数字字符),以及超出数据类型表示范围的情况下,上述函数均会抛异常,引发程序崩溃。
演示代码3.7:
int main()
{
string s1(" -123ab1");
string s2(" 1234");
string s3("+1234abc");
string s4("aa");
string s5("-123.12");
string s6("100.111abc");
string s7("110.ab");
string s8("001234.5");
string s9("123456789987654321");
cout << "s1 = " << stoi(s1) << endl; //-123
cout << "s2 = " << stoi(s2) << endl; //1234
cout << "s3 = " << stoi(s3) << endl; //1234
//cout << "s4 = " << stoi(s4) << endl; //无法转换为int会抛异常
cout << "s5 = " << stod(s5) << endl; //-123.12
cout << "s6 = " << stod(s6) << endl; //110.111
cout << "s7 = " << stod(s7) << endl; //110
cout << "s8 = " << stod(s8) << endl; //1234.5
//cout << "s9 = " << stoi(s9) << endl; //超出int范围会抛异常
return 0;
}