当前位置:首页 » 《随便一记》 » 正文

C/C++ 初识string

27 人参与  2024年05月27日 08:15  分类 : 《随便一记》  评论

点击全文阅读




?????????????????????????????????????


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 保证的是迭代器指向的数据不能写

反向迭代器

rbegin 和 rend

均只读

代码:???

        

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插入字符

push 和 append

插入字符

但这两种方式都不常用

还是喜欢用重载运算符

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的头到尾,插入s2cout << 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 1string 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 + stringcout << ret1 << endl;string ret2 = s1 + "xxxxxx";//string + char*cout << ret2 << endl;string ret3 ="xxxxxx" + s1;//char* + stringcout << 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,创作不易,望读者三连三连三连支持?

重要的事情说三遍?


点击全文阅读


本文链接:http://zhangshiyu.com/post/114042.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1