【 C++ 】string类的常用接口说明


目录

1、string类的基础知识

2、string类的常用接口说明

        2.1、string类的成员函数

                 constructor(构造函数)

                 destructor(析构函数)

                 operator=(赋值)

                 string类对象的容量操作

                 迭代器

                 string类的元素访问

                 string类对象的遍历操作

                 string类对象的修改操作

        2.2、sring类非成员函数


1、string类的基础知识

  1. 字符串是表示字符序列的类
  2. 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作单字节字符字符串的设计特性。
  3. string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型(关于模板的更多信息,请参阅basic_string)。
  4. string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_traits和allocator作为basic_string的默认参数(根于更多的模板信息请参考basic_string)。
  5. 注意,这个类独立于所使用的编码来处理字节:如果用来处理多字节或变长字符(如UTF-8)的序列,这个类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。

总结:

  1. string是表示字符串的字符串类
  2. 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。
  3. string包含的头文件是#include<string>
  4. string是属于std命名空间的,using namespace std;
  5. string类是管理动态增长字符数组,这个字符串以\0结尾

2、string类的常用接口说明

2.1、string类的成员函数

constructor(构造函数)

函数名称 功能说明
1、string()(重点) 无参构造空的string类对象,即空字符串
2、string(const char* s) (重点) 带参的常量字符串初始化
3、string(const string&s) (重点) 拷贝构造函数
4、string (const char* s, size_t n) 对一个字符串的前n个初始化
5、string (size_t n, char c) 用 n 个 c 初始化
6、string (const string& str, size_t pos, size_t len = npos) 从pos位置处取len长度的字符进行拷贝构造
  • 1、string() 无参构造
string s1;//无参初始化
  • 2、string(const char* s) 带参构造
string s2("hello world!");//常量字符串初始化
  • 3、string(const string&s) 拷贝构造
string s2("hello world!");//常量字符串带参构造
string s3(s2);//拷贝构造
string s4 = s2;//也是拷贝构造
  • 4、string (const char* s, size_t n) 对一个字符串的前n个初始化
string s5("https://mp.csdn.net/", 5);//对该字符串的前5个初始化
cout << s5 << endl;//https
  • 5、string (size_t n, char c) 用n个c初始化
string s6(5, 'k');//用5个k初始化
cout << s6 << endl;//kkkkk
  • 6、string (const string& str, size_t pos, size_t len = npos) 从pos位置处取len长度的字符进行拷贝构造
string s2("hello world!");//带参拷贝构造
string s7(s2, 6, 5);//从s2字符串的第6个位置往后取5个字符初始化
cout << s7 << endl;//world

destructor(析构函数)

前面已经说过string类是管理动态增长字符数组,对于动态申请的空间,需要用到析构函数把它释放掉。不过这里我们无需操作,因为编译器会帮我们默认调用构造函数。


operator=(赋值)

void test_string2()
{
	string s1("hello");
	string s2("xxx");
	s1 = s2;//string 
	s1 = "kkk";//c-string 
	s1 = 'y';//character 
}

string类对象的容量操作

函数名称 功能说明
1、size(重点) 返回字符串有效字符长度
2、length 返回字符串有效字符长度
3、max_size 返回字符串最大长度
4、capacity 返回空间总大小
5、reserve(重点) 为字符串预留空间
6、resize(重点) 将有效字符的个数拆成n个,多出的空间用字符c填充
7、clear(重点) 清空有效字符
8、empty(重点) 检测字符串释放为空串,是返回true,否则返回false
  • 1、size

size返回的是有效字符的个数:

size_t size() const;
int main ()
{
  string str ("Test string");
  cout << str.size() << endl;//11
  return 0;
}
  •  2、length
size_t length() const;

length和size无本质之差,返回的都是字符串的长度。

void test_string1()
{
	string s("hello world");
	cout << s.length() << endl;//11
}

但是受到历史背景的影响,还是推荐用size合意。

  • 3、max_size
size_t max_size() const;

max_size返回的就是最大值 

  •  4、capacity
size_t capacity() const;

capacity返回的就是容量大小

void test_string6()
{
	string s("hello world");
	cout << s.capacity() << endl;//15
}
  •  5、reserve
void reserve (size_t n = 0);

reserve的特性:

  1. 请求将字符串容量适应计划的大小更改为最多 n 个字符的长度。
  2. 如果 n 大于当前字符串容量,则该函数会导致容器将其容量增加到 n 个字符(或更大)。
  3. 在所有其他情况下,它被视为收缩字符串容量的非约束性请求:容器实现可以自由地进行优化,并使字符串的容量大于n。
  4. 此函数对字符串长度没有影响,并且无法更改其内容。
  5. 利用reserve进行提前预留空间,可以减少扩容带来的损耗。

如下没有reserve预留空间:


很明显,普通版本的尾插会进行多次扩容,而频繁扩容会带来效率损失,可以加上reserve预留空间进行优化:


很清晰明了,加上了reserve提前预留空间大大减少了扩容的频次从而避免效率上的损失。

reserve只会改变容量,再看一组测试用例:

void test()
{
	string s("hello world");
	cout << s << endl; //hello world
	cout << s.size() << endl; //11
	cout << s.capacity() << endl; //15

	//reverse(n)当n大于对象当前的capacity时,将当前对象的capacity扩大为n或大于n
	s.reserve(20);
	cout << s << endl; //hello world
	cout << s.size() << endl; //11
	cout << s.capacity() << endl; //31

	//reverse(n)当n小于对象当前的capacity时,什么也不做
	s.reserve(5);
	cout << s << endl; //hello world
	cout << s.size() << endl; //11
	cout << s.capacity() << endl; //31
}
  • 6、resize

resize特性:

  1.  将字符串大小调整为 n 个字符的长度。
  2. 如果 n 小于当前字符串长度,则当前值将缩短为其前 n 个字符,并删除超出第 n 个字符的字符。
  3. 如果 n 大于当前字符串长度,则通过在末尾插入所需数量的字符以达到 n 大小来扩展当前内容。如果指定了 c,则新元素将初始化为 c 的副本,否则,它们是值初始化字符(空字符)。
void test()
{
	string s1("hello world");
	//resize(n)n小于对象当前的size时,将size缩小到n
	s1.resize(4);
	cout << s1 << endl; //hell
	cout << s1.size() << endl; //4
	cout << s1.capacity() << endl; //15

	string s2("hello world");
	//resize(n)n大于对象当前的size时,将size扩大到n,扩大的字符默认为'\0'
	s2.resize(20);
	cout << s2 << endl; //hello world
	cout << s2.size() << endl; //20
	cout << s2.capacity() << endl; //31

	string s3("hello world");
	//resize(n, char)n大于对象当前的size时,将size扩大到n,扩大的字符为char
	s3.resize(20, 'x');
	cout << s3 << endl; //hello worldxxxxxxxxx
	cout << s3.size() << endl; //20
	cout << s3.capacity() << endl; //31	
}
  • 7、clear

clear的本质就是清掉所有空间。

void test()
{
	string s("hello world");
	cout << s << endl;//hello world
	s.clear();
	cout << s.size() << endl;//0
	cout << s << endl;//空
}
  • 8、empty

void test()
{
	string s("hello world");
	cout << s << endl;//hello world
	s.clear();//清空有效字符
	if (s.empty())
		cout << "empty" << endl;//empty
}

迭代器

迭代器就是像指针一样的东西 

函数名称 功能说明
1、begin

将迭代器返回到开头

2、end 将迭代器返回到末尾
3、rbegin 返回一个逆序迭代器,它指向容器c的最后一个元素
4、rend 返回一个逆序迭代器,它指向容器c的第一个元素前面的位置
  • 1、begin

  • 2、end

 示例如下:

void test_string3()
{
    string s1("hello");
	string::iterator it = s1.begin();
	while (it != s1.end())
	{
		cout << *it << " ";//h e l l o
		it++;
	}
}

  • 3、rbegin

rbegin是一种逆向迭代器

 rbegin的特性如下:

  1. 返回指向字符串最后一个字符(即其反向开头)的反向迭代器。
  2. 反向迭代器向后迭代:增加它们会将它们移动到字符串的开头
  3. rbegin 指向成员末尾将指向的字符之前的字符。

要留心反向迭代器的++是往反方向走,区别于正向迭代器,而其根本原因等后续讲到模拟实现再深究。

  • 4、rend

 rend的特性如下:

  1. 返回一个反向迭代器,该迭代器指向字符串的第一个字符(被视为其反向结尾)前面的理论元素
  2. string::rbegin 和 string::rend 之间的范围包含字符串的所有字符(顺序相反)。

  • 5、const正向迭代器

普通迭代器是可读可写的,因此针对const修饰的特殊情况下我们不能如下操作:

因为我Func里的s是const修饰的,而Func里的迭代器又是可读可写的版本,属于权限放大,因此要进行修正:

//const正向迭代器
void Func(const string& s)
{
    //记得加上const_,使其对于const的函数
	string::const_iterator it = s.begin(); 
    /*
    或者使用auto自动推导类型
    auto it = s.begin();
	*/
	while (it != s.end())
	{
        //*it += 1; 不能写
		cout << *it << " ";
		it++;
	}
}
  • 6、const反向迭代器
//const反向迭代器
void Func(const string& s)
{
	string::const_reverse_iterator rit = s.rbegin();
    /*
    或者使用auto自动推导类型
    auto rit = s.rbegin();
	*/
    while (rit != s.rend())
	{
        //*rit += 1; 不能写
		cout << *rit << " ";
		++rit;
	}
}

string类的元素访问

函数名称 功能说明
1、operator[ ] 获取字符串的字符
2、at 获取字符串中的字符
  • 1、operator[ ]

operator[ ] 是获取字符串中的字符。实际的场景如下:

void test_string_4()
{
	string s1("hello");
	const string s2("world");
	s1[0] = 'x';
	s2[0] = 'y'; //err
}

因为s2是const修饰的,只读,所以不能修改,自然s2[0]就会出错,而s1可读可写。

  • 2、at

 at和operator[ ]的功能一样,都是访问pos位置的字符。

void test_string_4()
{
	string s1("hello");
	s1[0] = 'x';
    //等价于
	s1.at(0) = 'x';
}
  • 3、at和operator[ ]对比:

虽然at和[ ]的功能一致,但还是有差异的,at和[ ]在处理越界的情况是不同的:

operator[ ]:

  1. 如果 pos 小于字符串长度,则该函数永远不会引发异常(无抛出保证)。
  2. 如果 pos 等于字符串长度,则 const 版本永远不会引发异常(无 throw 保证)。
  3. 否则,它会导致未定义的行为。
  4. 请注意,使用返回的引用来修改超出界限的元素(包括 pos 处的字符)也会导致未定义的行为。

这里[ ]越界是通过断言来报错的:

at:

  1. 强保证:如果抛出异常,字符串中没有变化。
  2. 如果 pos 不小于字符串长度,则会引发out_of_range异常。

 这里简单演示下捕获异常的场景:


string类对象的遍历操作

函数名称 功能说明
1、operator[ ] (重点) 返回pos位置的字符,const string类对象调用
2、begin + end begin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器
3、rbegin + rend begin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器
4、范围for C++11支持更简洁的范围for的新遍历方式
  • 法1:operator[ ] 

有了[ ]的运算符重载,我们就可以实现像C语言一样的下标+[ ]去访问。

void test_string3()
{
    //法一://下标+[]
	string s1("hello");
	for (size_t i = 0; i < s1.size(); i++)
	{
		//s1.operator[](i);
		cout << s1[i] << " "; //h e l l o
	}
}
  • 法2:正向迭代器 begin + end
void test_string3()
{
	//法二:正向迭代器
    string s1("hello");
	string::iterator it = s1.begin();
	while (it != s1.end())
	{
		cout << *it << " ";//h e l l o
		it++;
	}
}

  • 法3:反向迭代器 rbegin + rend
void test_string3()
{
    //法三:反向迭代器
	string s1("hello");
	string::reverse_iterator rit = s1.rbegin();
	while (rit != s1.rend())
	{
		cout << *rit << " ";//o l l e h
		++rit;
	}
}

  • 法4:范围for
void test_string3()
{
    //法4:范围for
	string s1("hello");
	for (auto ch : s1)//auto自动取s1里的字符,自动++
	{
		cout << ch << " "; 
	}
	cout << endl;
}

范围for可以自动帮助我们实现这一整套循环,看着十分便捷。不过范围for的本质其实还是利用迭代器的原理。这里大家可以通过查看反汇编得知。


string类对象的修改操作

函数名称 功能说明
1、push_back 在字符串后尾插字符c
2、insert 指定位置插入
3、append 在字符串后追加一个字符串
4、operator+= 在字符串后追加字符串str
5、erase 删除字符或字符串
6、swap 交换
7、c_str 返回C格式字符串
8、find 从字符串pos位置开始往后找字符C,返回该字符的位置
9、substr 在str中从pos位置开始,截取n个字符,然后返回
10、rfind 从字符串pos位置开始往前找字符C,返回该字符的位置
  • 1、push_back 尾插字符
void push_back (char c);

作用:将字符 c 追加到字符串的末尾,使其长度增加 1。 

#include<iostream>
#include<string>
using namespace std;
int main()
{
	string str("word");
	cout << str << endl; //world
	str.push_back('s');
	cout << str << endl; //worlds
}
  • 2、insert 指定位置插入

#include<iostream>
#include<string>
using namespace std;
int main()
{
	string str("hello world");
	//头部插入一个字符:
	str.insert(0, 1, 'x'); 
	cout << str << endl; //xhello world
	//使用迭代器头插:
	str.insert(str.begin(), 1, 'y');
	cout << str << endl; //yxhello world
	//在第n个位置插入字符:
	str.insert(3, 1, 'x');
	cout << str << endl; //yxhxello world
	str.insert(str.begin() + 3, 1, 'k');
	cout << str << endl; //yxhkxello world
	//头插一个字符串:
	str.insert(0, "!!!!");
	cout << str << endl; //!!!!yxhkxello world
}
  •  3、append 追加字符串

#include<iostream>
#include<string>
using namespace std;
int main()
{
	string s1("hello");
	string s2(" every");
	//追加一个string对象
	s1.append(s2);
	cout << s1 << endl; //hello every
	//追加一个常量字符串
	s1.append("body");
	cout << s1 << endl; //hello everybody
	//用n个字符拼接
	s1.append(3, '!');
	cout << s1 << endl; //hello everybody!!!
}
  • 4operator+=

作用:通过在字符串的当前值末尾追加其他字符来扩展字符串

参数含义:

  1. str:一个字符串对象,其值在末尾复制。
  2. s:指向以空值终止的字符序列的指针。序列在字符串的末尾复制。
  3. c:一个字符,追加到字符串的当前值。
#include<iostream>
#include<string>
using namespace std;
int main()
{
	string str("hello");
	str += ' ';
	str += "world";
	cout << str << endl; //hello world
	string ptr = "!!!!";
	str += ptr;
	cout << str << endl; //hello world!!!!
}
  • 5、erase 删除

#include<iostream>
#include<string>
using namespace std;
int main()
{
	string str("hello world");
	//头删
	str.erase(str.begin());
	cout << str << endl; //ello world
	//头删指定字符:
	str.erase(str.begin() + 3);
	cout << str << endl; //ell world
	//从pos处位置删除n个字符:
	str.erase(2, 3);
	cout << str << endl; //elorld
	//利用缺省值,只给定删除的位置,往后全删:
	str.erase(2);
	cout << str << endl; //el
	str.erase(0);
	cout << str << endl; //空
}
  • 6、swap 交换:
void swap (string& str);

注意:

  1. 通过 str(另一个字符串对象)的内容交换容器的内容。长度可能不同。
  2. 调用此成员函数后,此对象的值是调用前 str 的值,str 的值是调用前此对象具有的值。
  3. 请注意,存在一个具有相同名称的非成员函数,swap,使用与此成员函数类似的优化重载该算法。
#include<iostream>
#include<string>
using namespace std;
int main()
{
	string s1("hello world");
	string s2("HELLO WORLD");
	s1.swap(s2); //库里的swap 效率高 本质:交换指针
	swap(s1, s2);//全局的swap 效率低 本质:深拷贝
}
  • 7、c_str 返回C格式字符串​​​​​​​

#include<iostream>
#include<string>
using namespace std;
int main()
{
	string s1("hello");
	cout << s1 << endl; //hello
	cout << s1.c_str() << endl;//hello
}
  • 8、find

参数含义:

  1. str:另一个包含要搜索的主题的字符串
  2. pos:要在搜索中考虑的字符串中第一个字符的位置。如果此值大于字符串长度,则该函数永远不会找到匹配项。注意:第一个字符由值 0(而不是 1)表示:值为 0 表示搜索整个字符串。
  3. s:指向字符数组的指针。如果指定了参数 n (3),则要匹配的序列是数组中的前 n 个字符。否则(2),则期望以空终止的序列:要匹配的序列的长度由空字符的第一次出现确定。
  4. n:要匹配的字符序列的长度。
  5. c:要搜索的单个字符。

返回值含义:

  1. 第一个匹配项的第一个字符的位置。如果未找到匹配项,则该函数返回字符串::npos

 示例:

int main()
{
	
	string s1("https://blog.csdn.net/");
	string s2("blog");
	//正向搜索string类对象
	size_t pos1 = s1.find(s2);
	cout << pos1 << endl; //8
	//搜索字符
	size_t pos2 = s1.find('.');
	cout << pos2 << endl; //12
}
  • 9、substr

参数含义:

  1. pos要作为子字符串复制的第一个字符的位置。如果此值等于字符串长度,则该函数返回空字符串。如果此值大于字符串长度,则会抛出out_of_range。注: 第一个字符由值 0(而不是 1)表示。
  2. len:要包含在子字符串中的字符数(如果字符串较短,则使用尽可能多的字符)。值 string::npos表示在字符串末尾之前的所有字符。

返回值:具有此对象的子字符串的字符串对象。

int main()
{
	//要求取出文件的后缀
	string file("string.cpp.tar.zip");
	size_t pos = file.find('.');
	if (pos != string::npos)
	{
		//string suffix = file.substr(pos, file.size() - pos);
		string suffix = file.substr(pos);
		cout << file << "后缀:" << suffix << endl; //string.cpp.tar.zip后缀:.cpp.tar.zip
	}
	else
	{
		cout << "没有后缀" << endl;
	}
}

如果我要取出最后一个后缀.zip就需要用到rfind来完成:

  • 10、rfind

参数含义:

  1. str:另一个包含要搜索的主题的字符串
  2. pos:字符串中最后一个字符的位置,将被视为匹配的开始。任何大于或等于字符串长度的值(包括字符串::npos)都意味着将搜索整个字符串。注: 第一个字符由值 0(而不是 1)表示。
  3. s:指向字符数组的指针。如果指定了参数 n (3),则要匹配的序列是数组中的前 n 个字符。否则(2),则期望以空终止的序列:要匹配的序列的长度由空字符的第一次出现确定。
  4. n:要匹配的字符序列的长度。
  5. c:要搜索的单个字符。

示例:

int main()
{
	//要求取出文件的后缀
	string file("string.cpp.tar.zip");
	size_t pos = file.rfind('.');
	if (pos != string::npos)
	{
		//string suffix = file.substr(pos, file.size() - pos);
		string suffix = file.substr(pos);
		cout << file << "后缀:" << suffix << endl; //string.cpp.tar.zip后缀:.zip
	}
	else
	{
		cout << "没有后缀" << endl;
	}
}
int main()
{
	//取出url的域名
	string url1("https://cplusplus.com/reference/string/string/rfind/");
	string url2("https://blog.csdn.net/bit_zyx?spm=1000.2115.3001.5343");
	//协议 域名 uri
	string& url = url1;
	//取协议
	string protocol;
	size_t pos1 = url.find("://");
	if (pos1 != string::npos)
	{

		protocol = url.substr(0, pos1);
		cout << "protocol:" << protocol << endl; //https
	}
	else
	{
		cout << "非法url" << endl;
	}
	//取域名
	string domain; 
	size_t pos2 = url.find('/', pos1 + 3);
	if (pos2 != string::npos)
	{
		domain = url.substr(pos1 + 3, pos2 - (pos1 + 3));
		cout << "domain:" << domain << endl; //domain:cplusplus.com
	}
	//取uri
	string uri = url.substr(pos2 + 1);
	cout << "uri:" << uri << endl;//uri:reference/string/string/rfind/
}

2.2、sring类非成员函数

函数 功能说明

1、operator+

尽量少用,因为传值返回,导致深拷贝效率低
2、operator>> 输入运算符重载
3、operator<< 输出运算符重载
4、relational operators 大小比较
5、getline 获取一行字符串
  • 1、operator+

int main()
{
	string s1("hello");
	string s2(" world");
	//1、string类 + string类
	string s3 = s1 + s2;
	cout << s3 << endl; //hello world
	//2、string类 + 字符
	s3 = s1 + '!';
	cout << s3 << endl; //hello!
	//3、字符 + string类
	s3 = '!' + s2;
	cout << s3 << endl; //! world
	//4、string类 + 字符串
	s3 = s1 + "CSDN";
	cout << s3 << endl; //helloCSDN
	//5、字符串 + string类
	s3 = "!!!" + s2;
	cout << s3 << endl; //!!! world
}
  • 2、operator>> / operator<<
istream& operator>> (istream& is, string& str);
ostream& operator<< (ostream& os, const string& str);
int main()
{
	string str;
	cin >> str; //hello world
	cout << str << endl; //hello
}
  • 3、relational operators

string类对 ==、!=、<、<=、>、>=这些运算符进行了重载,并且支持string类和string类,string类和字符串间的比较,使用效果如下:

int main()
{
	string s1("abcd");
	string s2("efgh");
	cout << (s1 > s2) << endl;//0
	cout << (s1 < s2) << endl;//1
	cout << (s1 >= s2) << endl;//0
	cout << (s1 <= s2) << endl;//1
}
  • 5、getline

将行从流转换为字符串
  1. 从 is 中提取字符并将其存储到 str 中直到找到分隔字符 delim(或换行符 '\n',表示 (2))。
  2. 如果在 is 中到达文件末尾,或者在输入操作期间发生其他错误,则提取也会停止。
  3. 如果找到分隔符,则会提取并丢弃它(即不存储它,下一个输入操作将在它之后开始)。
int main()
{
	string s1;
	getline(cin, s1); //hello world
	cout << s1 << endl;//hello world
	string s2;
	getline(cin, s2, 'r'); //输入hello world
	cout << s2 << endl; //输出hello wo
}

猜你喜欢

转载自blog.csdn.net/bit_zyx/article/details/125116890