STL精讲:string类

大家好,这里是彩妙呀~

本篇博客会带着大家来学习STL中的string类。

严格来说string不算是STL里,而是c++库中的函数,但按照功能分配上,彩妙吧string归类为STL中。

string类是处理字符序列的核心容器,提供动态内存管理和丰富的成员函数。

其设计支持高效字符串操作,避免了传统C风格字符串的常见陷阱。

大家可以点击这个联机来看彩妙自己实现的string类,帮助你深入了解

目录

为什么要学习string类?

对比:c语言中的字符串

小部分说明

标准库中的string类

string类的构造函数

string的遍历

与c语言类似,string可以使用[]来访问数组中的元素:operator[]重载

使用迭代器来遍历容器中的元素

什么是迭代器?

string::begin

string::end

使用正向迭代器实现string类的遍历

string::rbegin

string::rend

反向迭代器(迭代器另一种类模版):reverse_iterator

使用反向迭代器实现string类的遍历

[使用const来修饰迭代器 const_iterator](#使用const来修饰迭代器 const_iterator)

使用string类内部接口at()

string::at()

[范围for (c++11中出现)](#范围for (c++11中出现))

string类中的Capacity:容量模版

[求string中所存储的数量/字符字数string::size() - string::lenth()](#求string中所存储的数量/字符字数string::size() - string::lenth())

为什么效果一样,但是名称不一样呢?

改变string字符串中字符数大小:string::resize()

​编辑

求string类的容量:string::capacity

string字符串的扩容:string::reserve

判断string字符串是否为空:string::empty()

清空string字符串中所有内容:string::clear

string字符串中插入/删除元素(字符)的接口

string::push_back

string::append

[string::operator +=(最常用)](#string::operator +=(最常用))

插入接口:string::insert

string::erase:删除指定位置/长度的字符

string::replace:替换指定位置/长度的字符

string::swap:交换两个字符串的内容

C++11新增:string::pop_back:删除最后一个字符

string类中其他的相关操作

[1. string::c_str:获取C风格字符串(const char*),兼容C语言接口](#1. string::c_str:获取C风格字符串(const char*),兼容C语言接口)

string::data:获取字符串数据指针(C++11后与c_str几乎无区别,早期无末尾'\0')

string类中查找函数集合

经典函数string::find:从左到右查找子串/字符,返回首次出现的索引(没找到返回string::npos)

string::substr:截取子串

string::compare:比较两个字符串


为什么要学习string类?

对比:c语言中的字符串

在C语言中,字符串实际上是以空字符( ' \0 ' )结尾的字符数组。这种表示方式带来了许多不便:

cpp 复制代码
#include <stdio.h>
#include <string.h>

int main() {
    char str1[20] = "Hello";
    char str2[20] = "World";
    char result[40];
    
    // 字符串连接
    strcpy(result, str1);
    strcat(result, " ");
    strcat(result, str2);
    
    printf("%s\n", result);  // 输出: Hello World
    
    // 需要手动管理内存
    char *dynamic_str = (char*)malloc(100 * sizeof(char));
    if(dynamic_str != NULL) {
        strcpy(dynamic_str, "Dynamic string");
        printf("%s\n", dynamic_str);
        free(dynamic_str);  // 必须手动释放内存
    }
    
    return 0;
}

C语言字符串的主要问题:

  • 手动内存管理:需要预先分配足够的空间

  • 容易出错:容易发生缓冲区溢出

  • 功能有限:需要依赖标准库函数

  • 不够直观:需要记住很多函数名和参数顺序

小部分说明

  1. 在c++类中,存在很多的函数冗余:例如string类的插入方式有多种,但是实际运用时有些函数重载却不常用,那是因为C++在迭代更新时,要兼容老版本的函数,所以除非万不得已,这些老一点的函数基本上不会舍弃(可以参考python2 -> python3所发生的事情:为什么 Python 3.0 设计成不与 Python 2.X 兼容?主要有哪些地方需要突破才导致这一决定?为什么 Python 3.0 设计成不与 Python 2.X 兼容?所以,本博客再讲接口时,有些不常用的接口就不在介绍,感兴趣的小伙伴可以通过彩妙提供的链接自己学一下~

标准库中的string类

我们可以通过C/C++标准库网页链接来查询string的声明:string类是C++标准库中的一个类模板实例化,定义在<string>头文件中。它封装了字符序列,并提供了丰富的成员函数来操作字符串。

string的接口统计下来有100+,但是常用的大概在20+左右,所以彩妙也会介绍常用的string类的接口。如果碰到不常用的,也可以通过https://cplusplus.com/reference/网站来查阅学习。

string类的构造函数

在参考文档中,我们发现string有7中构造函数(本篇博客讨论C++98知识,C++11可能会有拓展,不太适合新手学习):

cpp 复制代码
#include <iostream>
#include <string>
#include <vector>

int main() {
    // 1. 默认构造函数
    std::string str1;
    
    // 2. 拷贝构造函数
    std::string str2("Hello");
    std::string str3(str2);
    
    // 3. 子字符串构造函数
    std::string str4(str2, 1, 3);      // 从位置1开始,取3个字符:"ell"
    std::string str5(str2, 2);         // 从位置2开始到结尾:"llo"
    
    // 4. C风格字符串构造函数
    std::string str6("C++ String");    //等同于std::string str6 = "C++ String"
    
    // 5. 字符序列构造函数
    std::string str7("C++ Programming", 3);  // 取前3个字符:"C++"
    
    // 6. 填充构造函数
    std::string str8(5, 'A');          // "AAAAA"
    std::string str9(10, '-');         // "----------"
    
    // 7. 范围构造函数(使用迭代器)
    std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
    std::string str10(vec.begin(), vec.end());  // "Hello"
    
    std::string temp = "World";
    std::string str11(temp.begin() + 1, temp.end() - 1);  // "orl"
   
    return 0;
}

在这里,比较重要(常用)的构造函数有以下几种 --- 参考文献 ---:

  • string()

    构造空的string类对象(空字符串)

  • string(const char s) *

    使用C风格字符串构造string类对象

  • string(size_t n, char c)

    构造包含n个字符c的string类对象

  • string(const string& s)

    拷贝构造函数

cpp 复制代码
#include <iostream>
#include <string>

int main() {
    // 1. string() - 构造空的string类对象(空字符串)
    std::string str1;
    
    // 2. string(const char* s) - 使用C风格字符串构造string类对象
    std::string str2("Hello World");
    const char* cstr = "C++ String";
    std::string str3(cstr);
    
    // 3. string(size_t n, char c) - 构造包含n个字符c的string类对象
    std::string str4(5, 'A');        // "AAAAA"
    std::string str5(10, '*');       // "**********"
    
    // 4. string(const string& s) - 拷贝构造函数
    std::string str6(str2);          // 拷贝str2的内容
    std::string str7 = str3;         // 这也调用拷贝构造函数
    
    // 5. 额外演示:混合使用
    std::string str8(str2, 6, 5);    // 从位置6开始取5个字符:"World"
    
    // 输出所有字符串
    std::cout << "str1 (空字符串): \"" << str1 << "\"" << std::endl;
    std::cout << "str2 (C风格字符串1): \"" << str2 << "\"" << std::endl;
    std::cout << "str3 (C风格字符串2): \"" << str3 << "\"" << std::endl;
    std::cout << "str4 (5个'A'): \"" << str4 << "\"" << std::endl;
    std::cout << "str5 (10个'*'): \"" << str5 << "\"" << std::endl;
    std::cout << "str6 (拷贝str2): \"" << str6 << "\"" << std::endl;
    std::cout << "str7 (拷贝str3): \"" << str7 << "\"" << std::endl;
    std::cout << "str8 (子字符串): \"" << str8 << "\"" << std::endl;
    
    return 0;
}

string底层成员因该是这样的:

cpp 复制代码
protected:
    char* _str;
    size_t _size;
    size_t _capacity;

说完了初始化(实例化),下面就要来了解怎么遍历string数组:

string的遍历

string有多种遍历的方式:

与c语言类似,string可以使用[]来访问数组中的元素:operator[]重载

按照,这个重载有两种形态:

cpp 复制代码
char& operator[] (size_t pos);
const char& operator[] (size_t pos) const;

目的是返回给定位置处的字符的引用。这样就可以通过c语言中,使用变量来逐个遍历string字符串中的元素啦:

cpp 复制代码
// 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;
}

他的底层逻辑大概是这样的(自写,如有错误可以指正):

cpp 复制代码
char& operator[](size_t i) {
        assert(i<_size);
        return _str[i];
    }

使用迭代器来遍历容器中的元素

什么是迭代器?

迭代器(Iterator) 是C++中用于遍历容器(如vector、list、string等)中元素的对象。它类似于指针,提供了访问容器元素的统一接口。

我们可以把迭代器类比成指针:迭代器所处位置就是指针指向位置。但是迭代器不完全(不一定是)是指针(包括智能指针),所以在使用的时候我们要自己做判断。迭代器_参考文献

cpp 复制代码
std::string::iterator 迭代器名称 = 对应接口函数(例如.begin)

std::string::iterator it_be=str.begin()

std::string::iterator it_en=str.end()

//如果要使用迭代器来表述string字符串,和平常数组类似:使用 * 解引用即可
int main() {
    string str("123231123123123qweqweqweqwe3asdadadsad");
    // 使用迭代器来遍历字符串
    //定义一个string类型的迭代器 it 并指向开头
    string::iterator it = str.begin();
    for (; it != str.end(); ++it) {
        //使用解引用 * 来获取string的内容
        cout << *it << endl;
    }

    return 0;
}

下图是string类中所使用的迭代器:

这里我们要认识string的两个接口:begin与end

string::begin

参考文献

返回的是指向字符串第一个字符的迭代器。

string::end

参考文献

返回一个指向字符串末尾字符的迭代器。

内部实现细节我们在后续博客中,会给大家呈现。(但是在日常中,我们只用关心这些都行是干什么用的就行,不必深究)

了解完这两个基础的接口,我们就可以实现一个基础的迭代器的应用:正向迭代器

使用正向迭代器实现string类的遍历

顾名思义:使用迭代器从开头遍历到结尾,就是正向迭代器:

cpp 复制代码
  for (string::iterator it = str.begin(); it != str.end(); ++it) {
        //使用解引用 * 来获取string的内容
        cout << *it << endl;
    }

而反向迭代器与之同理:有两个接口来提供给你迭代器所使用的地址:

string::rbegin

相当于给begin加了个reverse(反转),使得迭代器所指向的内容为string字符串的最后一个字符的下一个字符 ' \0 '。

string::rend

相当于给end加了个reverse(反转),使得迭代器所指向的内容为string字符串的第一个字符。

而这两个接口一起实现了反向迭代器:

反向迭代器(迭代器另一种类模版):reverse_iterator

想要调用者两个接口,不能正常的去使用iterator,而是要在其前面加上reverse_来说明这个迭代器是一个

使用反向迭代器实现string类的遍历

顾名思义:使用迭代器从结尾遍历到开头,可以实现字符串的倒置:

cpp 复制代码
 string str("123231123123123qweqweqweqwe3asdadadsad");
    // 使用迭代器来遍历字符串
    //定义一个string类型的迭代器 it 并指向开头
    for (string::reverse_iterator it = str.rbegin(); it != str.rend(); ++it) {
        //使用解引用 * 来获取string的内容
        cout << *it << endl;
    }

使用const来修饰迭代器 const_iterator

如果我们的string被const修饰,那么无论正向与反向遍历,都要加上const_前缀来修饰迭代器:

cpp 复制代码
 const string st1 = "text string";
    for (string::const_iterator it = st1.begin(); it != st1.end(); ++it) {
        cout << *it << endl;
        
    }

反向要在reverse_iterator钱再加一个const_修饰。

使用string类内部接口at()

string::at()

这个函数的本质与operator [] 作用差不多,区别是at()函数中有存在函数越界的判断。

范围for (c++11中出现)

范围for 是C++11中提出来的,与迭代器相似(底层就是使用迭代器实现的),可以自动获取每一个值。

cpp 复制代码
string st2 = "text string";
    for (auto it : st2) {
        cout << it << endl;
    }

//如果确认值类型,可以直接换为其对应类型
string st2 = "text string";
    for (char it : st2) {
        cout << it << endl;
    }

在这里,it的类型为auto,其意思为自动推导:编译器通过后面 : 类型 来自动推导这里要使用迭代器的类型(迭代器在STL容器中通用,例如后面的list,vector等等)),从而实现字符赋值,自动迭代,自动判断结束。

注意::

  • 迭代器范围for从性能来说没有区别,区别只在于写起来方便罢了。
  • 在使用范围for尝试修改值时,修改的值不会改变原对象的值(迭代器却可以修改,原理是因为迭代器类似于指正,而范围for相当于调用迭代器,把值拷贝到自身)。
  • 如果需要使用范围for修改值,只需要在auto后加一个引用&:auto& it 即可。

适用地方:简化代码---string::iterator it=auto it(编译器会自动推导其类型,写代码就不需要码太多字了(在map章节体现的非常明显))

string类中的Capacity:容量模版

在string类中,有多个模版来求string字符串中的大小,容量的相关内容string类文档说明

求string中所存储的数量/字符字数string::size() - string::lenth()

对于效果而言,两个函数没有什么区别,都是返回string字符串的长度:

为什么效果一样,但是名称不一样呢?

在C++发展中,string的出现时间要早于STL,而STL容器中所有求大小/长度之类的接口,统一使用string::size(),string为了适配STL容器,也就有了string::size()接口。

所以,为了方便日常编程,求数量之类的都使用::size()接口。

cpp 复制代码
    string st2 = "text string";
    cout << st2.size() << endl;

改变string字符串中字符数大小:string::resize()

string::resize参考文档,这个接口我们可以直接去修改string字符串现有字节的数量:

  1. 当改变的大小 n 小于 字符串所保存数量的大小时:直接取前n个字符作为保留,后面所有的字符做舍弃;
  2. 如果改变大小 n 大于等于 字符串保存数量大小,并小于string字符串容量大小:这个操作相当于尾部插入元素,即图片中函数重载的第二个函数。当没有传char c时,自动按 ' '尾插。
  3. 如果改变大小 n 大于string字符串容量大小:直接执行扩容,并走2步骤

上面的论证大家可以自己写代码尝试,彩妙就不细讲了。

求string类的容量:string::capacity

string::capacity文档说明,这个接口返回出string字符串的容量。那么怎么知道string类怎么扩容呢?

cpp 复制代码
void text() {
    string s;
    size_t ts = s.capacity();
    for (int i = 0; i < 1e3; i++) {
        s.push_back('a');
        if (ts!=s.capacity()) {
            cout << "chang string capasity" << ts << endl;
            ts = s.capacity();
        }
    }

}

我们运行代码:

chang string capasity15

chang string capasity30

chang string capasity60

chang string capasity120

chang string capasity240

chang string capasity480

chang string capasity960

发现,在string类中,扩容逻辑:在申请内存不够用时,变申请本次容量大小的两倍,在进行深拷贝,从而完成扩容。

注意:不同平台编译器的扩容策略不一样:

**VS下是开始使用1.5倍扩容;**而g++是2倍扩容

要着重注意!

string字符串的扩容:string::reserve

string::reverse扩容文档,这个接口是为了减少频繁的扩容,因而降低运行效率,所开发的一个接口:我们可以自己手动扩容。

例如:我们做算法题,有些题目要求字符串的范围(例如1e8之类的)我们就可以直接手动扩容这么大的空间,进而增加运行效率。

cpp 复制代码
void _string_capasity() {
    string s;
    s.reserve(1000);
    cout << s.capacity() << endl;

    s.reserve(2000);
    cout << s.capacity() << endl;
    //在给string扩容时,只能向大扩容。这个接口本质是修改------capasity数值,而非直接增长
    //所以在传入比_capacity小的值,将不做处理
    //但是,上面的结论在不同的编译器有不同的处理方法:
    //有的编译器是把大小缩减至于size一样的大小才回停止下降。
    s.reserve(10);
    cout << s.capacity() << endl;
}
/*
结果:
1000
2000
2000
*/

判断string字符串是否为空:string::empty()

string::empty()参考文档,判断当前的字符串是否为空串,如果是返回值为true(非0),反之,则是0。

cpp 复制代码
#include <iostream>
#include <string>

std::string readLinesUntilEmptyWhile() {
    std::string content;
    std::string inputLine;
    
    std::cout << "开始输入(输入空行结束):\n";
    
    while (true) {
        std::getline(std::cin, inputLine);
        
        if (inputLine.empty()) {
            break;  // 遇到空行退出循环
        }
        
        content += inputLine + '\n';
    }
    
    return content;
}

清空string字符串中所有内容:string::clear

string::clear参考文档,直接消除字符串所有的元素,使得字符串变为一个空串:

cpp 复制代码
void echoLinesUntilDot() {
    char c;
    std::string buffer;
    
    std::cout << "请输入多行文本,以点号(.)结束输入:\n";
    
    do {
        // 读取一个字符
        c = std::cin.get();
        // 将字符添加到缓冲区
        buffer += c;
        
        // 如果遇到换行符
        if (c == '\n') {
            // 输出当前行
            std::cout << buffer;
            // 清空缓冲区准备下一行
            buffer.clear();
        }
    } while (c != '.');  // 遇到点号时停止
    
    // 注意:点号本身会被添加到缓冲区,但不会输出(因为输出只在换行时发生)
}

string字符串中插入/删除元素(字符)的接口

要在string字符串后插入元素(尾插)有多个接口:

string::push_back

string::push_back参考文档,使用这个接口可以直接在string字符串后尾插一个字符。注意:只能尾插一个字符,所以这个接口在日常编码时不常用。

cpp 复制代码
//  push_back:追加单个字符
    string str3 = "Hi";
    str3.push_back('!');  // 只能追加单个char类型字符
    cout << "3. push_back 结果: " << str3 << endl;  // 输出: Hi!

string::append

string::append参考文档,由于这个接口的函数重载过多,就不全部展示。

这个接口主要的目的就在于:可以更加灵活的追加string字符串(可以指定长度,追加子串等。)

cpp 复制代码
 // append:更灵活的追加(可指定长度、追加子串等)
    string str2 = "I love";
    str2.append(" C++");          // 追加完整字符串
    cout << " append 结果1: " << str2 << endl;  // 输出: I love C++
    str2.append(" Programming", 0, 4);  // 追加子串(从索引0开始,取4个字符)
    cout << " append 结果2: " << str2 << endl;  // 输出: I love C++ Prog

这个接口在特定场景下使用的比较多。

string::operator +=(最常用)

string::operator+=参考文档,这个运算符重载的接口可以让我们直接在string字符串结尾处追加内容。因为他写起来比较方便,所以是最常用的一个接口:

cpp 复制代码
 // operator+=:向字符串末尾追加内容(最常用)
    string str1 = "Hello";
    str1 += " World";  // 追加字符串
    cout << "operator+= 结果: " << str1 << endl;  // 输出: Hello World

插入接口:string::insert

string::insert参考文档,这个函数也有多个函数重载,目的是为了在字符串任意位置插入元素。

这个接口在头插与中间插入时可能会有效率损耗,所以要慎重使用

cpp 复制代码
    // insert:在指定位置插入内容
    string str5 = "HelloWorld";
    str5.insert(5, " ");  // 在索引5的位置插入空格
    cout << "insert 结果: " << str5 << endl;  // 输出: Hello World

string::erase:删除指定位置/长度的字符

string::erase参考文档,这个接口可以删除指定位置与长度的字符,但是他与插入一样,在头删与中间删除有损效率,所以也是要慎重使用。

如果要消除pos全部的字符,可以不传npos 值,默认就是全部删除。

cpp 复制代码
 // erase:删除指定位置/长度的字符
    string str6 = "Hello World";
    str6.erase(5);        // 从索引5开始删除后面所有字符
    cout << "erase 结果1: " << str6 << endl;  // 输出: Hello
    str6 = "Hello World"; // 重置字符串
    str6.erase(5, 1);     // 从索引5开始,删除1个字符(删除空格)
    cout << "erase 结果2: " << str6 << endl;  // 输出: HelloWorld

string::replace:替换指定位置/长度的字符

string::replace参考文档,可以做string字符串的替换,底层实现比较复杂(后续将string类的底层实现会将),效率相对之下也不高,但在一些特定地方(例如查找替换,增删改查之类的应用算法题)与string::find()会有奇用:

cpp 复制代码
    // replace:替换指定位置/长度的字符
    string str7 = "I love Java";
    str7.replace(7, 4, "C++");  // 从索引7开始,替换4个字符为"C++"
    cout << "replace 结果: " << str7 << endl;  // 输出: I love C++

    //与find搭配用法
    string str-s = "hello world hello linux!";
    size_t pos = str-s.find(' ');
    while (pos != string::npos){
    str-s.replase(pos,1,"__");
    pos = str-s.find(' ');
    }

string::swap:交换两个字符串的内容

string::swap参考文档,这个接口实现了两个string字符串的交换,简单高效:

cpp 复制代码
//  swap:交换两个字符串的内容
    string str8_1 = "Apple";
    string str8_2 = "Banana";
    str8_1.swap(str8_2);
    cout << "swap 结果: str8_1=" << str8_1 << ", str8_2=" << str8_2 << endl;
    // 输出: str8_1=Banana, str8_2=Apple

C++11新增:string::pop_back:删除最后一个字符

string::pop_back参考文档,这个接口提供尾删新方法:

cpp 复制代码
    // pop_back:删除最后一个字符(C++11及以上支持)
    string str9 = "Hello!";
    str9.pop_back();  // 删除末尾的'!'
    cout << "9. pop_back 结果: " << str9 << endl;  // 输出: Hello

string类中其他的相关操作

1. string::c_str:获取C风格字符串(const char*),兼容C语言接口

string::c_str参考文档,这个结构返回指向字符串对象值的 C 字符串所表示形式的指针,主要目的是为了兼容c语言:

cpp 复制代码
    string str = "Hello World! 123 Hello";

    // 1. c_str:获取C风格字符串(const char*),兼容C语言接口
    const char* c_style_str = str.c_str();
    cout << "1. c_str 结果: " << c_style_str << endl;

    // 用途示例:传给需要const char*的函数(如C语言的printf)
    printf("   c_str 用途演示: %s\n", c_style_str);

string::data:获取字符串数据指针(C++11后与c_str几乎无区别,早期无末尾'\0')

string::data参考文档,基本与第一个介绍的c_str()没多大区别:

cpp 复制代码
    string str = "Hello World! 123 Hello";
    // 2. data:获取字符串数据指针(C++11后与c_str几乎无区别,早期无末尾'\0')
    const char* data_ptr = str.data();
    cout << "2. data 结果: " << data_ptr << endl;
    // 说明:C++11标准后,data()返回的指针也带末尾'\0',和c_str()功能一致
    // 区别仅在于语义:c_str强调"C风格字符串",data强调"原始数据"

string类中查找函数集合

经典函数string::find:从左到右查找子串/字符,返回首次出现的索引(没找到返回string::npos)

string::find参考文档,这个接口作用就是在string字符串中查找字符/子串,

而且由于版本发展,我们这里也会出现多个版本的find函数,但本质作用都是要去查找字符/子串,如果感兴趣,大家可以自己查阅参考文档:

cpp 复制代码
    string str = "Hello World! 123 Hello";

 // 5. find:从左到右查找子串/字符,返回首次出现的索引(没找到返回string::npos)
    size_t find_pos = str.find("Hello"); // 查找子串"Hello"
    if (find_pos != string::npos) {
        cout << "5. find 找到'Hello'的位置: " << find_pos << endl; // 输出: 0
    }
    find_pos = str.find("Test"); // 查找不存在的子串
    cout << "   find 没找到'Test'的返回值: " << (find_pos == string::npos ? "npos" : to_string(find_pos)) << endl;

    // 6. rfind:从右到左查找,返回最后一次出现的索引
    size_t rfind_pos = str.rfind("Hello");
    cout << "6. rfind 找到最后一个'Hello'的位置: " << rfind_pos << endl; // 输出: 15

    // 7. find_first_of:查找指定字符集中任意字符的出现位置
    //可以看做find_any_of,作用是找到所有字符。
    //这个名字可能有点问题,但这个接口用的不多
    size_t first_of_pos = str.find_first_of("12345"); // 找'1'/'2'/'3'/'4'/'5'中出现的位置
    cout << "7. find_first_of 找到数字的位置: " << first_of_pos << endl; // 输出: 13

    // 8. find_last_of:查找指定字符集中任意字符的最后一次出现位置
    //同7
    size_t last_of_pos = str.find_last_of("lo"); // 找'l'或'o'最后出现的位置
    cout << "8. find_last_of 找到'l/o'最后位置: " << last_of_pos << endl; // 输出: 18(第二个Hello的'o')

    // 9. find_first_not_of:查找第一个不在指定字符集中的字符位置
    string num_str = "12345abc678";
    size_t first_not_of_pos = num_str.find_first_not_of("0123456789");
    cout << "9. find_first_not_of 找到非数字的位置: " << first_not_of_pos << endl; // 输出: 5

    // 10. find_last_not_of:查找最后一个不在指定字符集中的字符位置
    size_t last_not_of_pos = num_str.find_last_not_of("0123456789");
    cout << "10. find_last_not_of 找到最后一个非数字的位置: " << last_not_of_pos << endl;   
    // 输出: 7

string::substr:截取子串

string::substr参考文档,这个接口可以获取我们要从哪个位置开始的、多长的子串(参数1:起始索引,参数2:截取长度,缺省则到末尾):

cpp 复制代码
    string str = "Hello World! 123 Hello";
    string sub1 = str.substr(6, 5); // 从索引6开始,截取5个字符
    cout << "11. substr 结果1: " << sub1 << endl; // 输出: World
    string sub2 = str.substr(13); // 从索引13开始,截取到末尾
    cout << "   substr 结果2: " << sub2 << endl; // 输出: 123 Hello

string::compare:比较两个字符串

string::compare参考文献,这个接口作用是比较两个字符串

返回值:0=相等,<0=当前串小,>0=当前串大

cpp 复制代码
    string str_a = "Apple";
    string str_b = "Banana";
    string str_c = "Apple";
    
    int cmp_ab = str_a.compare(str_b);
    int cmp_ac = str_a.compare(str_c);
    cout << "12. compare 结果: " << endl;
    cout << "   Apple vs Banana: " << cmp_ab << "(<0表示Apple更小)" << endl; // 输出负数
    cout << "   Apple vs Apple: " << cmp_ac << "(=0表示相等)" << endl; // 输出0

getline (string)函数:得到一行的字符串内容

getline (string)参考文档,string字符串在使用cin输入数据时,有一个缺陷:遇到 ' '(空格)后才会终止读取(可以传第三个参数来改变这个终止flag):

cpp 复制代码
#include <iostream>
#include <string>

int main ()
{
  std::string name;

  std::cout << "输入你的名字:";
  //通常情况下,传入第一个参数是cin流(输入流),第二个才是string字符串。
  std::getline (std::cin,name);
  std::cout << "Hello, " << name << "!\n";

  //特殊情况下,可以改变输入终止的flag
  //遇到 '*'时停止读取
  std::getline(std::cin,name,'*')
  std::cout << "Hello, " << name << "!\n";

  return 0;
}

本篇到这里就结束了,喜欢文章的小伙伴可以关注一下彩妙,我们下一篇再见~

相关推荐
小屁猪qAq5 小时前
创建型之单例模式
开发语言·c++·单例模式
郝学胜-神的一滴5 小时前
深入解析以太网帧与ARP协议:网络通信的基石
服务器·开发语言·网络·程序人生
王老师青少年编程5 小时前
GESP(C++)考级(七级&八级)真题及详细题解(汇总版)
c++·题解·真题·gesp·csp·七级·八级
lingran__5 小时前
C语言动态内存管理详解
c语言·开发语言
haokan_Jia5 小时前
【java使用LinkedHashMap进行list数据分组写入,顺序并没有按照原始顺序,原因分析】
java·开发语言·list
凯子坚持 c5 小时前
C++大模型SDK开发实录(三):流式交互协议SSE解析与httplib实现原理
开发语言·c++·交互
小屁猪qAq5 小时前
从单例模式说动态链接
c++·单例模式·链接·编译
ghie90905 小时前
基于MATLAB的多旋翼无人机多机编队仿真实现
开发语言·matlab·无人机
少控科技5 小时前
QT新手日记026
开发语言·qt