C/C++ 初识string



🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉


1、什么是STL

STL(standard template libaray-标准模板库):是C++标准库的重要组成部分,不仅是一个可复用的组件库,而且 是一个包罗数据结构与算法的软件框架。

1.1、STL的六大组成 

2、string

2.1、string文档介绍

默认成员函数

迭代器

容量相关的

C语言中:string

        串

        字符数组,可以扩容,可以增删改查。(更符合的信息都是字符串存的,比如身份证,名字,地址...

        数据和方法分离(空间和使用)

                

C++中:string

2.2、string的构造

提供了7个

2.3、string类对象的常见构造 

        就前3个常用,其余的不常用。

        带参构造、无参构造、拷贝构造

关于npos

        

size_t表示无符号整形,所以npos为FFFF FFFFH

2.4、string类对象的容量操作

知识点

operator[]

底层:

       

一个指针,一个大小,一个容量(用来扩容)。

2.5、我们如何遍历自定义类型的字符串呢?

方法1:size( ) + operator[ ]

        这里自定义类型s1可以像数组一样访问,而且访问越界有报错。

void test_string3()
{
	string s1("hello world");

	//计算s1大小
	cout << s1.size() << endl;


	//访问字符串
	// 方法1:operator[]
	//自定义类型s1,可以像数组一样访问
	//可读,可写,可修改
	
	//可读
	for (size_t i = 0; i < s1.size(); i++)
	{
		//写全
		//cout << s1.operator[i] << " ";
		cout << s1[i] << " ";
	}
	cout << endl;

	s1[0] = 'x';
	cout << endl;
	
	//可修改
	for (size_t i = 0; i < s1.size(); i++)
	{
		s1[i]++;
	}
}

 放法2:迭代器

先看代码👇👇👇

void test_string4()
{
	string s1("hello world");

	//iterator定义在类域里,只能在类域中搜索。
	string::iterator it1 = s1.begin();
	while (it1 != s1.end())
	{
		cout << *it1 << " ";
		++it1;
	}
	cout << endl;
}

代码中出现begin( ) 和 end( )

如何理解呢?

解析:

         我们可以把begin和end看作指针begin指第一个字符,end指最后一个字符的下一个位置。结合代码,begin把第一个字符的地址给给it1,然后解引用it1取到值,++it1走到下一个位置,知道遇到end()就停止。

注意:

        \0是表示字符,不是有效字符

        [ begin,end ) 为左闭右开区间,

        

对比方法一和方法二:

        方法一只适合下标访问,空间必须是连续的,所以只适用于string;

        方法二迭代器是主流的访问方式,

举例链表使用迭代器访问

        

总结:

        begin( ):任何容器返回第一个数据位置的iterator

        end( ):任何容器返回最后数据的下一个位置的iterator

方法3:范围for(所有容器都支持)

​
for (auto e : s1)
{
	cout << e << " ";

}
cout << endl;

​

自动取s1里的值,赋值给e,自动++,自动判断结束。

底层角度,就是迭代器

注意:

        这里是*it赋值给给e,赋值拷贝,并不会影响数据。

2.6迭代器iterator

普通迭代器const迭代器

我们先看一下const迭代器

如图,这里是一个const string,就不能用之前那种方式遍历了,

因为const string 是只读的,这里我们调用

修改代码:👇👇👇

总结:

        iterator 可读可写

        const_iterator 只读

        普通的迭代是给普通的string用的

        const的是给const对象用的,保护string不能修改数据,可以遍历

这里我们再深究一下:

        为什么const_iterator这洋写,而不是const iterator

        因为const iterator 保证的是迭代器本身不能写

        而const_operator 保证的是迭代器指向的数据不能写

反向迭代器

rbeginrend

均只读

代码:👇👇👇

        

string s2("hello world");
string::reverse_iterator it2 = s2.rbegin();
while (it2 != s2.rend())
{
	cout << *it2 << " ";
	++it2;
}
cout << endl;

底层重载operator++来实现自定义类型的++。

迭代器总结

共有4中迭代器

        iterator const_iterator reverse_iterator const_reverse_iterator

差异就在读和写部分

        iterator 可读可写

        const_iterator只读

        reverse_iterator 只读

        const_reverse_iterator 只读

2.7string插入字符

pushappend

插入字符

但这两种方式都不常用

还是喜欢用重载运算符

assign

 assign:赋值字符串,但赋值通常用operator=,运算符重载。

这里我们浅浅了解一下,熟悉一下看文档。

insert、erase和repalce

insert插入

string的insert慎用,

因为时间复杂度很大,效率低。

库中没有给出头插一个字符怎某写,

这里我们这样:👇👇👇

char ch = 'y';
cin >> ch;
s2.insert(0, 1, ch);//头插一个
cout << s2 << endl;

insert的使用:👇👇👇

void test_string8()
{
	string s1("hello world");
	cout << s1 << endl;

	s1.assign("11111");
	cout << s1 << endl;

	string s2("hello world");
	s2.insert(0, "xxxx");
	cout << s2 << endl;

	
	char ch = 'y';
	cin >> ch;
	s2.insert(0, 1, ch);//头插一个
	cout << s2 << endl;

	s2.insert(s2.begin(), 'y');
	cout << s2 << endl;

	s2.insert(s2.begin(), s1.begin(), s1.end());//把s1的头到尾,插入s2
	cout << s2 << endl;
}

erase和replace

erase:

慎用,效率低。

replace:

把我的一部分替换

erase的使用👇👇👇

void test_string9()
{
	string s1("hello world");
	cout << s1 << endl;

	s1.erase(0, 1);//去掉第一个字符
	cout << s1 << endl;

	s1.erase(5, 100);//去掉5 - 100,后边没有也不报错,
	cout << s1 << endl;

	//replace效率不高,慎用,和insert类似,需要挪动数据
	string s2("hello world");
	s2.replace(5, 1, "%20");
	cout << s2 << endl;
}

说到替换,那把所有空格都替换怎某实现呢?

代码👇👇👇

//把空格全部替换成%20
//方法一:循环替换,但时间复杂度大
string s3("hello world hello bit");
for (size_t i = 0; i < s3.size();)
{
	if (s3[i] == ' ')
	{
		s3.replace(i, 1, "%20");
		i += 3;//跳过%20这三个字符
	}
	else
	{
		i++;
	}
}
cout << s3 << endl;
//方法二:以空间换时间,
string s4("hello world hello bit");
string s5;
for (auto ch : s4)
{
	if (ch != ' ')
	{
		s5 += ch;
	}
	else
	{
		s5 += "%20";
	}
}
cout << s5 << endl;

这里提供了俩方法:

        1.循环替换

        2.另辟蹊径

我们这里不推荐方法一循环替换,为什么呢?

        因为时间复杂度非常大,方法二以时间换空间。

看到这里,我们小试牛刀,练练手。

小试牛刀

.仅仅反转字母(双指针)

.字符串中的第一个唯一一个字符(映射)

.验证回文串(回文:对称),题目:只判断字母

max_size()

字符串的最大长度(用的不多),因为类型开不出这样大的空间

reserve和resize

reserve改变capacity

resize改变size,也可以改变capacity

reserve:扩容(提前开好空间,不适用于缩容)

        在vs中不能缩容(看编译器)

string s1("11111111111");
string s2("1111111111111111111111111111111");

cout << s1.capacity() << endl;

s1.reserve(100);
cout << s1.capacity() << endl;
s1.reserve(20);
cout << s1.capacity() << endl;

reserve的使用

void TesePushBack()
{
	string s;
	s.reserve(200);
	s[100] = 'x';
}

开了空间是不能直接赋值的,因为[ ]在底层调用operator [ ],会调用size,而reverse只改变capacity,所以这时还需要初始化size

resize的使用

void test_string11()
{
	string s1;
	s1.resize(5);
	s1[4] = '3';
	s1[3] = '4';
	s1[2] = '5';
	s1[1] = '6';
	s1[0] = '7';
}

resize不会填充前面已有数据,会在后面补

string s2("hello world");
s2.resize(20, 'x');

at

越界,异常可以捕获。

string s2("hello world");

//s2[30];
//s2.at(30);
try
{
	s2.at(30);
}
catch (const exception& e)
{
	cout << e.what() << endl;
}

s2[30] 直接报错,

s2.at(30)会捕获异常

operator是暴力型的,at是温柔型的。

c_str()

文件读取

#define _CRT_SECURE_NO_WARNINGS 1

string file("Test.cpp");
FILE* fout = fopen(file.c_str(), "r");//获取底层指向char的那个指针。
char ch = fgetc(fout);
while (ch != EOF)
{
	cout << ch;
	ch = fgetc(fout);
}

find( )、rfind( )和substr( )

find()

查找字符

这里掌握第一个和第四个

第一个:查找一个striing,pos位置

第四个:从pos位开始找,一个字符

find() : 正在查找

rfind():倒着查找

分开网址

取https

取gitee.com

find : 从pos1 + 3位置开始找 ‘ / ’.

substr : 从pos + 3开始取,取pos2 -(pos1+3)这么长.

size_t pos2 = url.find('/', pos1 + 3);//从pos1 + 3开始找‘/’
string url2 = url.substr(pos1 + 3, pos2 - (pos1 + 3));//左开右闭,从pos + 3开始取,取pos2 -(pos1+3)这么长
cout << url2 << endl;

取剩下的

find_first_of  、 find_last_of

find_first_of 

.把aeiou 全部变为 *

.相当于c中的strtok( ),

用的非常少

void test_string13()
{
	std::string str("Please, replace the vowels int this sentence by asterisk.");
	std::size_t found = str.find_first_of("aeiou");
	while (found != std::string::npos)
	{
		str[found] = '*';
		found = str.find_first_of("aeiou", found + 1);
	}

	std::cout << str << '\n';

}

find_last_of

从后忘前找

operator +

.提供了

        string + string  

        string + char* 

        char* + string

        string + char

//operator + 写成了全局函数,为了保证ret3能够实现
void test_string14()
{
	string s1 = "hello";
	string s2 = "world";

	string ret1 = s1 + s2;//string + string
	cout << ret1 << endl;

	string ret2 = s1 + "xxxxxx";//string + char*
	cout << ret2 << endl;

	string ret3 ="xxxxxx" + s1;//char* + string
	cout << ret3 << endl;


	//字典序比较,比较ascii码值
	cout << (s1 < s2) << endl;
}

getline()

先看一题:字符串最后一个单词的长度_牛客题霸_牛客网 (nowcoder.com)

原因在于cin和scanf在遇到空格后就结束了(流提取问题),

规定空格或换行是多个值之间的分割

所以这里用getline(cin,str),获取一行中包含空格,不能用 >>

这里我们看一个代码:持续获得字符串

string str;
//默认规定遇到空格或换行是多个值之间的分割
while (cin >> str)
{
	cout << str << endl;
}

当输入换行时就停止了,那程序如何停止呢?

ctrl + c ,直接杀进程

ctrl + z + 换行

to_string

字符串转整形

c中 : stoi字符串转整型 和 itos整型转字符串

c++ : to_string

转整型,浮点型...

int main()
{
	int x = 3, y = 0;
	cin >> x >> y;
	string str = to_string(x+y);//转整型
	cout << str << endl;
}
int z = stoi(str);//字符串转整型

3.vs和g++下string结构的说明

3.1vs下

        注意:下述结构是在32位平台下进行验证,32位平台下指针占4个字节。

        vs下string的结构 string总共占28个字节,内部结构稍微复杂一点,先是有一个联合体,联合体用来定义string中字。

        符串的存储空间:

                当字符串长度小于16时,使用内部固定的字符数组来存放

                当字符串长度大于等于16时,从堆上开辟空间

        这种设计也是有一定道理的,大多数情况下字符串的长度都小于16,那string对象创建好之后,内部已经有了16个字符数组的固定空间,不需要通过堆创建,效率高。

其次:还有一个size_t字段保存字符串长度,一个size_t字段保存从堆上开辟空间总的容量

最后:还有一个指针做一些其他事情。

3.2g++下

        G++下,string是通过写时拷贝实现的,string对象总共占4个字节,内部只包含了一个指针,该指针将来指向一块堆空间,

        内部包含了如下字段:

                ·空间总大小

                ·字符串有效长度

                ·引用计数

指向堆空间的指针,用来存储字符串。


🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉


last but not least,创作不易,望读者三连三连三连支持💖

重要的事情说三遍💖

  • 31
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 25
    评论
评论 25
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值