为什么要学习string类??
C语言中,字符串是以"/0"结尾的一些字符的集合;为了操作方便,C标准库中提供了一些关于str的库函数,但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问;为了简单、方便、快捷,基本都使用string类,很少有人去使用C库中的字符串操作函数。
string类的特点:
1.string是表示字符串的字符串类;
2.string类的接口与常规的接口基本相同,再添加了一些专门用来操作string的常规操作;
3.string类的内部结构为:
class string
{
char* _str;
size_t _size;
size_t _capacity;
};
相当于一个动态增容的管理字符串的顺序表;
4.string类不能操作多字节或变长字符的序列;
string类的使用场景:
string是C++标准库的一个重要的部分,主要用于字符串处理。可以使用输入输出流方式直接进行操作,也可以通过文件等手段进行操作。同时C++的算法库对string也有着很好的支持,而且string还和c语言的字符串之间有着良好的接口;(在使用string类时,必须包含#include头文件以及using namespace std;)
具体的关于string类的所有函数的操作及用法,可以多在官网上进行查找学习:
www.cplusplus.com/reference/string/string/?kw=string
string类的具体操作:
1.string类的常用接口说明:
本文主要总结一些关于string类的常用操作符,具体的关于string类的操作可在官网进行查看;
(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) | 拷贝构造函数 |
#include <iostream>
#include <string>
using namespace std;
//最常用的两个初始化;
int main(){
std::string s1; //构造空的string类对象;
std::string s2("hello world"); //string类的初始化;
std::string s3(s2); //拷贝构造;
std::string s4(100,X); //打印
std::string s5(s2,2,7); //s2中从第二个数起向后取7个字符;
cout<<s1<<endl;
cout<<s2<<endl;
cout<<s3<<endl;
cout<<s4<<endl;
cout<<s5<<endl;
return 0;
}
(2)string类对象的容量(Capacity)操作:
函数名 | 功能说明 |
---|---|
size | 返回字符串有效字符长度 |
length | 返回字符串有效字符长度 |
capacity | 返回空间总大小 |
empty | 检测字符串释放为空串,是返回true,否则返回false |
clear | 清空有效字符 |
reserve | 为字符串预留空间 |
resize | 将有效字符的个数改成n个,多出的空间用字符c填充 |
#include<iostream>
#include <string>
using namespace std;
int main(){
std::string str("hello world");
cout <<str.size()<<endl; //字符串中有效字符的个数;
cout<<str.length()<<endl; //字符串中有效字符的个数;
cout<<str.max_size()<<endl; //表示最大值有多大,在32位操作系统下永远是42亿;
cout<<str.capacity<<endl; //容量是动态增容的,并不是要多少就给多少;
cout <<str<<endl;
str.clear(); //clear的作用是清掉所有数据,并不会清理空间;清理空间是交给析构函数进行处理的;
cout<<str.size()<<endl;
cout<<str.length()<<endl;
cout<<str.capacity<<endl;
return 0;
}
该程序输出的结果是:
关于reserve和resize的功能:
resize功能实现程序;(resize把空间开好后,这些空间就全部已经初始化了;)
#include <iostream>
#include <string>
using namespace std;
int main ()
{
std::string str ("I like to code in C");
std::cout << str << '\n';
unsigned sz = str.size();
str.resize (sz+2,'+');
std::cout << str << '\n';
str.resize (14);
std::cout << str << '\n';
return 0;
}
程序输出结果为:
reserve的功能实现程序;
(reserve只管去开空间,如果要让数据进去得用push_back将数据加入到size中)
#include <iostream>
#include <string>
using namespace std;
int main(){
std::string s("hello");
//测试reserve是否会改变string中的有效元素个数;
s.reserve(100);
cout << s.capacity() << endl;
cout << s.size() << endl;
// 测试reserve参数小于string的底层空间大小时,是否会将空间缩小;
s.reserve(50);
cout << s.size() << endl;
cout << s.capacity() << endl;
cout << s << endl;
return 0;
}
程序结果为:
//利用reserve提高插入数据的效率,避免增容带来开销;
void TestPushBack() {
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';
}
}
}
void TestPushBackReserve() {
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';
}
}
}
总结:
(1)size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一 致,一般情况下基本都是用size();
(2)clear()只是将string中有效字符清空,不改变底层空间大小;如果要改变空间大小需要析构函数;
(3) resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的 元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大 小,如果是将元素个数减少,底层空间总大小不变;
(4)reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于 string的底层空间总大小时,reserver不会改变容量大小;
(3)string类对象的访问及遍历操作:
函数名 | 功能说明 |
---|---|
operator[ ] | 返回post位置的字符,const string类对象的调用 |
begin+end | begin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器 |
rbegin+rend | 返回反向迭代器以反向开始 + 将反向迭代器返回到反向结束 |
范围for | C++11支持更简洁的范围for的新遍历方式 |
operator[]代码的实现操作:
// string::operator[]
#include <iostream>
#include <string>
int main ()
{
std::string str ("Test string");
for (int i=0; i<str.length(); ++i)
{
std::cout << str[i];
}
return 0;
}
编辑并运行
//operator[]主要用于获取字符串中的字符;如果字符串对象是const限定的,则该函数返回一个const char&;否则,它返回一个char&;
#include <iostream>
#include<stdlib.h>
#include <string>
using namespace std;
int main(){
std::string str1("hello world");
const std:: string str2("Hello world");
cout << str1 << " " << str2 << endl;
cout << str1[0] << " " << str2[0] << endl;
str1[0] = 'H';
cout << str1 << endl;
//str2[0] = 'h'; //代码编译失败,因为const类型对象不能被修改;
cout << str2<< endl;
system("pause");
return 0;
}
程序输出的结果为:
正向迭代器:begin+end代码实现操作:
(begin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器)
#include <iostream>
#include <string>
int main ()
{
std::string str ("Test string");
for ( std::string::iterator it=str.begin(); it!=str.end(); ++it){
std::cout << *it;
}
std::cout << '\n';
return 0;
}
代码实现结果:
反向迭代器:rbegin+rend代码实现操作:
(返回反向迭代器以反向开始 + 将反向迭代器返回到反向结束)
#include <iostream>
#include <string>
#include <stdlib.h>
int main()
{
std::string str("I am here!!!");
for (std::string::reverse_iterator rit = str.rbegin(); rit != str.rend(); ++rit){
std::cout << *rit;
}
system("pause");
return 0;
}
代码实现结果:
string类对象的遍历操作:
void TestString() {
string s("hello Bit");
// 3种遍历方式:
// 需要注意的以下三种方式除了遍历string对象,还可以遍历是修改string中的字符,
// 另外以下三种方式对于string而言,第一种使用最多
// 1. for+operator[]
for(size_t i = 0; i < s.size(); ++i)
cout<<s[i]<<endl;
// 2.迭代器
string::iterator it = s.begin();
while(it != s.end()) {
cout<<*it<<endl;
++it;
}
string::reverse_iterator rit = s.rbegin();
while(rit != s.rend()){
cout<<*rit<<endl;
}
// 3.范围for
for(auto ch : s){
cout<<ch<<endl;
}
(4)string类对象的修改操作:
函数名称 | 功能说明 |
---|---|
push_back | 在字符串末尾插入字符c |
append | 在字符串后在追加一个字符串 |
operator+= | 在字符串后追加字符串str |
c_str | 返回c 格式字符串 |
find +npos | 从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置 |
rfind | 从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置 |
substr | 在str中从pos位置开始,截取n个字符,然后将其返回 |
push_back以及append操作用法:
#include <iostream>
#include <string>
using namespace std;
int main(){
std::string s("hello world");
s.push_back('!'); //在str字符串后加!;
cout<<s<<endl;
std::string str;
std::string str2="Writing ";
std::string str3="print 10 and then 5 more";
//直接追加一个str2的字符串;
str.append(str2); // "Writing "
//后面追加str3第6个字符开始的3个字符串;
str.append(str3,6,3); // "10 "
//追加字符串形参的前5个字符
str.append("dots are cool",5); // "dots "
//直接添加
str.append("here: "); // "here: "
//添加10个'.'
str.append(10u,'.'); // ".........."
//添加str3迭代器范围的字符串
str.append(str3.begin()+8,str3.end()); // " and then 5 more"
//最后这个比较特殊,意思是添加5个'A',实际上参数里面的65对应的asc码就是65
str.append<int>(5,0x2E); // "....."
return 0;
}
operator+=操作用法:
//operator的三种常用操作:
//string&operator + =(const string&str);
//string&operator + =(const char * s);
//string&operator + =(char c);
//通过在其当前值的末尾附加其他字符来扩展字符串;
#include <iostream>
#include <string>
int main ()
{
std::string name ("John");
std::string family ("Smith");
name += " K. "; // c-string
name += family; // string
name += '\n'; // character
std::cout << name;
return 0;
}
//运行结果为:John K. Smith
string的字符串操作:
find函数:主要是查找一个字符串是否在调用的字符串中出现过,大小写敏感;
#include <iostream>
#include <string>
using namespace std;
int main ()
{
std::string str ("There are two needles in this haystack with needles.");
std::string str2 ("needle");
std::size_t found = str.find(str2); //从str中找str2中的字符串在str中的第几位;
if (found!=std::string::npos){ //npos是string里的一个静态成员变量
std::cout << "first 'needle' found at: " << found << '\n';
}
found=str.find("needles are small",found+1,6);
if (found!=std::string::npos){
std::cout << "second 'needle' found at: " << found << '\n';
}
found=str.find("haystack");
if (found!=std::string::npos){
std::cout << "'haystack' also found at: " << found << '\n';
}
found=str.find('.');
if (found!=std::string::npos){
std::cout << "Period found at: " << found << '\n';
}
str.replace(str.find(str2),str2.length(),"preposition");
std::cout << str << '\n';
return 0;
}
输出的程序结果为:
rfind函数:就是找最后一个出现的匹配字符串,返回的位置仍然是从前往后数的;
#include <iostream>
#include <string>
#include <cstddef>
using namespace std;
int main ()
{
std::string str ("The sixth sick sheik's sixth sheep's sick.");
std::string key ("sixth");
std::size_t found = str.rfind(key);
if (found!=std::string::npos){
str.replace (found,key.length(),"seventh");
}
std::cout << str << '\n';
return 0;
}
输出结果为:
substr函数: 在str中从pos位置开始,截取n个字符,然后将其返回;
#include <iostream>
#include <string>
int main ()
{
std::string str="We think in generalities, but we live in details.";
//从str字符串的第三个字符开始,截取5个字符,然后进行打印;
std::string str2 = str.substr (3,5); //think;
std::size_t pos = str.find("live"); //查找live是否出现;
std::string str3 = str.substr (pos); //从live 开始截取所有的字符;
std::cout << str2 << ' ' << str3 << '\n';
return 0;
}
程序执行结果为:
(5)string类非成员操作:
函数 | 功能说明 |
---|---|
operator+ | 尽量少用,因为传值拷贝导致深拷贝效率低 |
operator>> | 输入运算符重载 |
operator<< | 输出运算符重载 |
getline | 获取一行字符串 |
relational operator | 比较大小 |
operator函数:用于连接字符串:
#include <iostream>
#include <string>
using namespace std;
int main ()
{
std::string firstlevel ("com");
std::string secondlevel ("cplusplus");
std::string scheme ("http://");
std::string hostname;
std::string url;
hostname = "www." + secondlevel + '.' + firstlevel;
url = scheme + hostname;
std::cout << url << '\n';
return 0;
}
//输出结果为:http://www.cplusplus.com