怎么制作网站图片不显示,做论坛网站需要什么备案,技能培训,网页设计公司兴田德润实力强片头
嗨#xff01;好久不见~ 今天我们来学习C的Sting类#xff0c;不过#xff0c;在学习它之前#xff0c;我们先来对STL库有一个简单的了解。 STL#xff08;standard template library--标准模板库#xff09;#xff0c;是C标准库的重要组成部分#xff0c;不仅是…片头
嗨好久不见~ 今天我们来学习C的Sting类不过在学习它之前我们先来对STL库有一个简单的了解。 STLstandard template library--标准模板库是C标准库的重要组成部分不仅是一个可复用的组件库而且是一个包罗数据结构与算法的软件框架。
STL的版本
原始版本 Alexander Stepanov 、 Meng Lee 在惠普实验室完成的原始版本本着开源精神他们声明允许 任何人任意运用、拷贝、修改、传播、商业使用这些代码无需付费。唯一的条件就是也需要向原 始版本一样做开源使用。 HP 版本 -- 所有 STL 实现版本的始祖。 P.J版本 由 P. J. Plauger 开发继承自 HP 版本被 Windows Visual C 采用不能公开或修改缺陷可读性比较低符号命名比较怪异。 RW版本 由 Rouge Wage 公司开发继承自 HP 版本被 C Builder 采用不能公开或修改可读性一 般。 SGI版本 由 Silicon Graphics Computer Systems Inc 公司开发继承自 HP 版 本。被 GCC(Linux) 采用可 移植性好可公开、修改甚至贩卖从命名风格和编程 风格上看阅读性非常高。 我们后面学习 STL 要阅读部分源代码主要参考的就是这个版本 STL有六大组件 一、为什么学习string类 1.1 C语言中的字符串 C语言中字符串是以\0结尾的一些字符的集合为了操作方便C标准库中提供了一些str系列的库函数但是这些库函数与字符串是分离开的不太符合OOP思想面向对象编程而且底层空间需要用户自己清理稍不留神可能还会越界访问。 例如C语言中的strcpy和strcat函数 strcpy把一个字符串的内容复制到另一个字符串中 ①空间必须自己提供并且要保证2块空间至少一样大 ②如果如果目标字符串的空间不足以容纳源字符串就会导致内存溢出的问题 ③ 在使用strcpy函数时应保证目标字符串有足够的空间 strcat将一个字符串拼接到另一个字符串的末尾 ①从头到尾找源字符串的\0如果源字符串很长那么效率会非常低下 ②目标字符串必须有足够的空间来容纳源字符串否则会导致缓冲区溢出的问题 在OJ中有关字符串的题目基本以string类的形式出现而且在常规工作中为了简单方便快捷基本都使用string类很少有人去使用C库中的字符串操作函数。 二、标准库中的string类
2.1 string类
1字符串是表示字符序列的类
2标准的字符串类提供了对此类对象的支持其接口类似于标准字符容器的接口但添加了专门用于操作单字节字符字符串的设计特性
3string类是使用char即作为它的字符类型使用它的默认char_traits和分配器类型
4string类是basic_string模板类的一个实例它使用char来实例化basic_string模板类并用char_traits和allocator作为basic_string的默认参数
5注意这个类独立于所使用的编码来处理字节如果用来处理多字节或变成字符如UTF-8的序列这个类的所有成员如长度或大小以及它的迭代器将仍然按照字节而不是实际编码的字符来操作 总结 string是表示字符串的字符串类该类的接口与常规容器的接口基本相同再添加了一些专门用来操作string的常规操作string在底层实际是basic_string模板类的别名typedef basic_stringcharchar_traitsallocatorstring不能操作多字节或者变长字符的序列 在使用string类时必须包含#include头文件以及using namespace std;
2.2 string类对象的常见构造
1.string类对象的常见构造
constructor函数名称功能说明string() 默认构造 (不传参就可以调用)构造空的string类对象即空字符串string(const char* s) 带参构造用C-string来构造string类对象string(const string s) 拷贝构造拷贝构造函数string(size_t n,char c)string类对象中包含n个字符cstring(const string s,size_t pos,size_t len npos)从pos位置开始拷贝len个字符去构造初始化string(const char* s,size_t n)拷贝字符串的前n个字符
我们先把前3个给试试看~
void test_string1() {string s1; //默认构造string s2(hello world!); //带参构造string s3(s2); //拷贝构造//支持流插入和流提取cout s1 endl;cout s2 endl;cout s3 endl;cin s1;cout s1 endl;
}
运行结果如下 3string(const string s,size_t pos,size_t len npos)函数 string(const string s,size_t pos,size_t len npos) 我们可以尝试一下 那么当len为npos是什么意思呢 如果我们不传第3个参数的值那么len就默认是npos就从pos位置开始拷贝42亿个字符。
但是根本不可能啊所以当出现这种省略第3个参数的情况编译器默认拷贝到源字符串的结尾。
那如果我传递的第3个参数的值大于源字符串的长度会怎么样很明显也是拷贝到字符串的结束位置。
void test_string2() {string s1(beautiful!);string s2(s1, 4, 6);string s3(s1, 4);string s4(s1, 4, 30);cout s2 endl;cout s3 endl;cout s4 endl;
} 总结 ①len后面的字符长度有多少拷贝多少拷贝到结尾 ②缺省参数npos是整型最大值一定大于后面的长度不传第3个参数默认拷贝到结尾 5string(const char* s,size_t n)函数 我们测试一下
void test_string3() {string s1(hello world!,5);cout s1 endl;
}6string(size_t n,char c)函数 我们测试一下
void test_string4() {string s1(10,x);cout s1 endl;
} 小试牛刀
看看这2个函数是不是感觉很熟悉
void test_string5() {//带参构造string s1(hello world!);//隐式类型转换string s2 hello world!;
}其实这2个看似相同但是里面的逻辑是不一样的~ 那如果我想用引用符号呢
void test_string6() {//带参构造string s1(hello world!);//隐式类型转换string s2 hello world!;//引用的是生成的临时对象//临时对象具有常性,因此,需要在前面添加constconst string s3 hello world!;
}
此时s3为临时对象的别名因此这里是直接构造不需要优化 2.3 string类对象的容量操作
函数名称功能说明size返回字符串有效长度length返回字符串有效长度capacity返回空间总大小empty检测字符串是否为空串是返回true否则返回falseclear清空有效字符 reserve为字符串预留空间resize将有效字符的个数改成n个多出的空间用字符c填充
1size函数和length函数 size_t size() const; 返回字符串有效字符长度
void test_string7() {string s1(hello world);cout s1.size() endl;cout s1.length() endl;
} 注意1.size()和length()方法底层实现原理完全相同引入size()的原因是为了与其他容器的接口保持一致一般情况下都是用size()
2capacity函数 size_t capacity() const; 返回字符串的容量
void test_string30() {string s1(hello);cout s1.capacity() endl;
} 我们可以看看在vs上的扩容
void TestPushBack() {string s;size_t sz s.capacity();cout capacity init: sz \n;cout making s grow:\n;for (int i 0; i 200; i) {s.push_back(c);if (sz ! s.capacity()) {sz s.capacity();cout capacity changed: sz \n;}}
} 我们可以看到capacity比实际空间少一个有一个多的空间是预留给\0的
如何扩容C标准并没有规定取决于编译器实现
3empty函数 bool empty() const; 检测字符串是否为空串为空返回true否则返回false
void test_string31() {string s1;string s2(hello);cout s1.empty() endl;cout s2.empty() endl;
} 4clear函数 void clear(); 用于清空有效字符不改变字符串容量的大小
void test_string32() {string s1(hello);s1.clear();cout s1.size() endl;
} 5reserve函数 void reserve(size_t n 0); 为字符串预留空间只影响capacity不影响size
void test_string33() {string s1(hello);cout s1.capacity() endl;s1.reserve(10);cout s1.capacity() endl;s1.reserve(50);cout s1.capacity() endl;
} 如果n比原容量小则不做改变
在vs上常常会开比n更大一些的空间
所以如果知道需要多少空间我们可以使用reserve函数提前开好避免频繁扩容带来的不方便 6resize函数 void resize(size_t n); void resize(size_t n,char c); 将有效字符的个数修改为n并且如果n大于原来的_size多出来的地方用字符c填充不改变字符串容量的大小既可以影响size又可以影响capacity
如果没有给出字符c则用\0填充
void test_string34() {string s1(hello);cout s1.c_str() endl;s1.resize(2);cout s1.c_str() endl;s1.resize(10,x);cout s1.c_str() endl;
} 2.4 string类对象的访问及遍历操作
函数名称功能说明operator[]返回pos位置的字符const string类对象调用beginendbegin获取一个字符的迭代器end获取最后一个字符下一个位置的迭代器rbeginrendbegin获取一个字符的迭代器rend获取最后一个字符下一个位置的迭代器范围forC11支持更简洁的范围for的新遍历方式at返回字符串中pos位置的字符的引用back返回字符串最后一个字符的引用front返回字符第一个字符串的引用
如果我想遍历s1字符串该怎么做呢
①首先我们需要获取字符串的长度运用size函数
②调用operator[]函数重载可以使自定义类型像内置类型一样打印
void test_string8() {string s1(hello world);cout s1.size() endl;for (int i 0; i s1.size(); i) {cout s1[i] ;}cout endl;
} 它的底层逻辑大概是这个样子 引用返回不仅可以减少拷贝而且可以修改返回的对象
为什么可以用引用返回呢因为字符出了作用域并不会销毁它是在堆上开辟的空间返回的是堆上的字符引用相当于是这个字符的别名
那么还有另一种方法遍历字符串么有使用iterator迭代器~ void test_string9() {string s1(hello world);cout s1.size() endl;//遍历方式1: 下标[]for (int i 0; i s1.size(); i) {cout s1[i] ;}cout endl;//遍历方式2: 使用iterator迭代器string::iterator it1 s1.begin();while (it1 ! s1.end()) {cout *it1 ;it1;}cout endl;
} 此外我们还可以使用范围for对字符串进行循环遍历~
void test_string9() {string s1(hello world);cout s1.size() endl;//遍历方式1: 下标[]for (int i 0; i s1.size(); i) {cout s1[i] ;}cout endl;//遍历方式2: 使用iterator迭代器string::iterator it1 s1.begin();while (it1 ! s1.end()) {cout *it1 ;it1;}cout endl;//遍历方式3: 范围forfor (auto e : s1) {cout e ;}cout endl;
}
范围for的底层它就是迭代器。因此看上去有3种方法实质上就只有2种---operator[]和迭代器
使用范围for的时候是将s1里面的值依次拷贝给ee相当于是s1里面的值的一份临时拷贝对e进行修改不影响s1里面的值。如果我们需要通过e来改变s1里面的值需要传引用 //遍历方式3: 范围forfor (auto e : s1) {e;//将s1里面的每一个字符都1cout e ;}cout endl; 当然啦迭代器也分为被const修饰的和不被const修饰。 我们举一个例子假如字符串s1被const修饰也就是说字符串s1的内容不允许改变。 所以这里应该修改成这样
void test_string11() {const string s1(hello world);string::const_iterator st1 s1.begin();while (st1 ! s1.end()) {cout *st1 ;st1;}cout endl;
} 同时因为是iterator是被const修饰的因此它指向的内容不允许修改也就是不能对*st1进行修改 还有一种更简便的方法就是直接使用关键字auto来帮助我们自动匹配类型 正着遍历我们知道一些了反向遍历呢
那就要请出我们的一个朋友了----rbegin函数和rend函数
就拿刚刚的s1字符串举一个例子吧~ 我们可以尝试一下
void test_string12() {string s1(hello world);string::reverse_iterator st1 s1.rbegin();while (st1 ! s1.rend()) {cout *st1 ;st1;}cout endl;
} 同理 如果s1被const修饰的话reverse_iterator也应该被const修饰变成const_reverse_iterator
5at函数 char at(size_t pos); const char at(size_t pos)const; 返回字符串中pos位置的字符的引用
例如
void test_string35() {string s1(hello);for (int i 0; i s1.size(); i) {cout s1.at(i);}cout endl;
} 6 back函数 char back(); const char back() const; 返回字符串最后一个字符的引用
例如
void test_string36() {string s1(hello world!);cout s1.back() endl;
} 7 front函数 char front(); const char front() const; 返回字符串第一个字符的引用 例如
void test_string37() {string s1(hello world!);cout s1.front() endl;
} 好啦接下来我们想要对字符串s1里面的内容进行字典序排序该怎么做呢
首先我们需要包含一个头文件#includealgorithm 其次我们需要使用sort函数来帮助我们完成字符排序 void test_string13() {string s1(hello world);cout s1 endl;//s1按字典序排序sort(s1.begin(), s1.end());cout s1 endl;
}
排完序的结果如下 假设我不想让第一个字符和最后一个字符不参与排序只想让中间的字符进行排序怎么做
void test_string13() {string s1(hello world);cout s1 endl;//除了第一个和最后一个不参与,//其余的字符都要参与排序sort(s1.begin(), --s1.end());cout s1 endl;
}
排序结果如下 如果我只想要前面的hello进行排序那么区间就是下标[0,4]那么begin从0开始end为5
void test_string13() {string s1(hello world);cout s1 endl;//前5个字符排序sort(s1.begin(), s1.begin() 5);cout s1 endl;
} 片尾
今天我们学习了C之stirng类上希望看完这篇文章能对友友们有所帮助
求点赞收藏加关注
谢谢大家