【C++修炼之路】8. string类详解(二)

简介: 【C++修炼之路】8. string类详解(二)

11.剪切(substr)

string str = "The apple thinks apple is delicious";
//s.substr(pos,n)                      得到字符串s位置为pos后面的n个字符组成的串
string s1 = str.substr(4, 5);           // s1 = "apple"
//s.substr(pos)                        得到字符串s从pos到结尾的串
string s2 = str.substr(17);            // s2 = "apple is delicious"
string s3 = str.substr(4, 100);        // s3 = "apple thinks apple is delicious"
string s4 = str.substr(4, string::npos);// s4 = "apple thinks apple is delicious"


12.比较(compare)


两个字符串自左向右逐个字符相比(按ASCII值大小相比较),直到出现不同的字符或遇’\0’为止。若是遇到‘\0’结束比较,则长的子串大于短的子串,如:“9856” > “985”。如果两个字符串相等,那么返回0,调用对象大于参数返回1,小于返回-1。


微信图片_20230225132820.png

string str1 = "small leaf";
  string str2 = "big leaf";
  //s.compare(str)                     比较当前字符串s和str的大小
  cout << str1.compare(str2);                   // 1
  //s.compare(pos,n,str)               比较当前字符串s从pos开始的n个字符与str的大小
  cout << str1.compare(2, 7, str2);               // -1
  //s.compare(pos,n0,str,pos2,n)       比较当前字符串s从pos开始的n0个字符与str中pos2开始的n个字符组成的字符串的大小
  cout << str1.compare(6, 4, str2, 4, 4);           // 0
  //s.compare(pos,n0,cstr,n)           比较当前字符串s从pos开始的n0个字符与字符数组cstr中前n个字符的大小
  //此处不可将"big leaf"替换为str2
  cout << str1.compare(6, 4, "big leaf", 4);       // 1


13.交换(swap)


string str1 = "small leaf";
string str2 = "big leaf";
//或者str1.swap(str2)  ,输出结果相同
swap(str1,str2);        // str1 = "big leaf"     str2 = "small leaf"
swap(str1[0],str1[1]);  // str1 = "ibg leaf"


14.反转(reverse)


反转字符串。

string str = "abcdefghijklmn";
  string::iterator it1 = str.begin();
  string::iterator it2 = str.end();
  reverse(str.begin(), str.end());       // str = "nmlkjihgfedcba"
  reverse(it1, it2);                      // 又反转了一次:str = "abcdefghijklmn"

对于str.begin()和str.end()类型,其返回值可以看成指针,但实际上并不是指针,因此我们用char* 定义的it1和it2是不对的,其在不同的平台上有不同的含义,事实上其有着迭代器的作用。(下面讲解迭代器的使用)


15. 迭代器(iterator)


迭代器实际上是一个像指针一样的东西,这是对行为来说的。需要注意的是,这里不能用char*,虽然对于vs这个平台可以使用char*,但难保其他的平台是char*,因此迭代器的底层不一定是char*,这在模拟实现中将会详细介绍。(对于迭代器来说,只要会用string类,那么vector、list等容器与其方法是一样的)


15.1正向迭代器


//迭代器  -- 通用的访问形式
string s1("1234");
string::iterator it1 = s1.begin();//s.begin() 返回字符串s第一个字符的位置
while (it1 != s1.end())//s.end()  返回字符串s最后一个字符串的后一个位置
{
    *it1 += 1;
    ++it1;
}
it1 = s1.begin();
while (it1 != s1.end())
{
    cout << *it1 << " ";
    ++it1;
}           // 输出:2 3 4 5 


15.2反向迭代器

当然,还有反向迭代器,其访问顺序与默认的迭代器相反。

string s1("1234");
string::reverse_iterator rit = s1.rbegin();
while (rit != s1.rend())
{
    cout << *rit << " ";
    ++rit;
} // 输出: 4 3 2 1

此外,我们发现此定义名过长,因此我们也可以用auto去定义变量rit接收rbegin()。


15.3const迭代器


当我们需要只读的时候,为了避免改变其中的值,在迭代器使用时我们就会选择const迭代器,顾名思义const迭代器能够保护迭代指向的变量不被改变,那我们实际来看一下const迭代器如何使用:


void Print(const string& s)
{
  string::const_iterator it = s.begin();//正向
  while (it != s.end())
  {
    cout << *it << " ";
    it++;
  }
  cout << endl;
  string::const_reverse_iterator rit = s.rbegin(); //反向 可以用auto代替,即:auto rit = s.rbegin();
  while (rit != s.rend())
  {
    cout << *rit << " ";
    rit++;
  }
  cout << endl;
}
int main()
{
  string s1("1234");
  Print(s1);
  return 0;
}

微信图片_20230225133007.png


总结:迭代器经过上述的描述,一共有四种,即正向、反向、正向const、反向const,其不能混合定义,否则会出现错误。


16.搜索与查找(find等函数)


16.1 find()函数


微信图片_20230225133052.png

string str = "The apple thinks apple is delicious";     //长度34
string key = "apple";
//s.find(str)            查找字符串str在当前字符串s中第一次出现的位置
int pos1 = str.find(key);                  // 4
//s.find(str,pos)        查找字符串str在当前字符串s的[pos,end]中第一次出现的位置
int pos2 = str.find(key, 10);              // 17
//s.find(cstr,pos,n)     查找字符数组cstr前n的字符在当前字符串s的[pos,end]中第一次出现的位置
//此处不可将"delete"替换为str2(如果定义str2 = "delete")
int pos3 = str.find("delete", 0, 2);       // 26
//s.find(ch,pos)         查找字符ch在当前字符串s的[pos,end]中第一次出现的位置
int pos4 = str.find('s', 0);               // 15

16.2 rfind函数


微信图片_20230225133129.png

rfind与find的区别就是:find是从左往右找(即从前往后),而rfind是从后往前找。

string str = "The apple thinks apple is delicious";     //长度34
string key = "apple";
//s.rfind(str)            查找字符串str在当前字符串s中最后一次出现的位置
int pos5 = str.rfind(key);                 // 17
//s.rfind(str,pos)        查找字符串str在当前字符串s的[0,pos+str.length()-1]中最后一次出现的位置
int pos6 = str.rfind(key, 16);             // 4
//s.rfind(cstr,pos,n)     查找字符数组cstr前n的字符在当前字符串s的[0,pos+n-1]中最后一次出现的位置
//此处不可将"apple"替换为key
int pos7 = str.rfind("apple", 40, 2);      // 17
//s.rfind(ch.pos)         查找字符ch在当前字符串s的[0,pos]中最后一次出现的位置
int pos8 = str.rfind('s', 30);             // 24


16.3 find_xxx_of()函数(功能强大,但不常用)


xxx是需要确定的名字,因此下面将介绍这种类型的查找函数:(4个)


  1. find_first_of


微信图片_20230225133230.png


// s.find_first_of(str)                查找字符串str中的任意字符在当前字符串s中第一次出现的位置
int pos1 = str.find_first_of(key);                // 2
//s.find_first_of(str,pos)             查找字符串str中的任意字符在当前字符串s的[pos,end]中第一次出现的位置
int pos2 = str.find_first_of(key, 10);            // 11
//s.find_first_of(cstr,pos,n)          查找字符串str前n个任意字符在当前字符串s的[pos,end]中第一次出现的位置
//此处不可将"aeiou"替换为key
int pos3 = str.find_first_of("aeiou", 7, 2);      // 17
//s.find_first_of(ch,pos)              查找字符ch在当前字符串s的[pos,end]中第一次出现的位置
int pos4 = str.find_first_of('r', 0);             // 6


最好的解释就是举例子:


微信图片_20230225133259.png


即找到每个str中的字符进行替换,与find和rfind的区别是:此查找找的是字符串中的所有字符,而不是字符串。下面的几个同样如此:


  1. find_last_of


微信图片_20230225133343.png

//s.find_last_of(str)                 查找字符串str中的任意字符在当前字符串s中最后一次出现的位置
int pos1 = str.find_last_of(key);                      // 27
//s.find_last_of(str,pos)             查找字符串str中的任意字符在当前字符串s的[0,pos]中最后一次出现的位置
int pos2 = str.find_last_of(key, 15);                  // 11
//s.find_last_of(cstr,pos,n)          查找字符串str前n个任意字符在当前字符串s的[0,pos]中最后一次出现的位置
//此处不可将"aeiou"替换为key
int pos3 = str.find_last_of("aeiou", 20, 2);           // 17
//s.find_last_of(str)                 查找字符ch在当前字符串s的[0,pos]中最后一次出现的位置
int pos4 = str.find_last_of('r', 30);                  // 28


  1. find_first_not_of


微信图片_20230225133414.png


//s.find_first_not_of(str)             查找字符串str之外的任意字符在当前字符串s中第一次出现的位置
int pos1 = str.find_first_not_of(key);                 // 0
//s.find_first_not_of(str,pos)         查找字符串str之外的任意字符在当前字符串s的[pos,end]中第一次出现的位置
int pos2 = str.find_first_not_of(key, 10);             // 10
//s.find_first_not_of(cstr,pos,n)      查找字符串str前n个之外任意字符在当前字符串s的[pos,end]中第一次出现的位置
//此处不可将"aeiou"替换为key
int pos3 = str.find_first_not_of("aeiou", 7, 2);       // 7
//s.find_first_not_of(str)             查找字符ch之外任意字符在当前字符串s的[pos,end]中第一次出现的位置
int pos4 = str.find_first_not_of('r', 0);              // 0


  1. find_last_not_of


微信图片_20230225133448.png


//s.find_last_not_of(str)             查找字符串str之外的任意字符在当前字符串s中最后一次出现的位置
int pos1 = str.find_last_not_of(key);                  // 29
//s.find_last_not_of(str,pos)         查找字符串str之外的任意字符在当前字符串s的[0,pos]中最后一次出现的位置
int pos2 = str.find_last_not_of(key, 15);              // 15
//s.find_last_not_of(cstr,pos,n)      查找字符串str前n个之外任意字符在当前字符串s的[0,pos]中最后一次出现的位置
//此处不可将"aeiou"替换为key
int pos3 = str.find_last_not_of("aeiou", 20, 2);       // 20
//s.find_last_not_of(str)             查找字符ch之外任意字符在当前字符串s的[0,pos]中最后一次出现的位置
int pos4 = str.find_last_not_of('r', 30);              // 29



3.string类的应用



3.1 三种遍历方式


int main()
{
    string s1("1234");
  // 遍历他
  // 1、下标 []
  for (size_t i = 0; i < s1.size(); ++i)
  {
    s1[i]++;//实际上这是operator[]()的运算符重载
  }
  //s1[10];
  cout << s1 << endl;
  // 2、范围for
  for (auto& ch : s1)
  {
    ch--;
  }
  cout << s1 << endl;
  // 反转一下
  size_t begin = 0, end = s1.size() - 1;
  while (begin < end)
  {
    swap(s1[begin++], s1[end--]);
  }
  cout << s1 << endl;
  //reverse(s1.begin(), s1.end()); 算法头文件中的函数,直接使用即可。
  cout << s1 << endl;
  // 3、迭代器  -- 通用的访问形式
  string::iterator it1 = s1.begin();
  while (it1 != s1.end())
  {
    *it1 += 1;
    ++it1;
  }
  it1 = s1.begin();
  while (it1 != s1.end())
  {
    cout << *it1 << " ";
    ++it1;
  }
  cout << endl;
}


3.2替换空格


题目:替换空格


解法1:

对于这道题,如果按照c语言的方式会很麻烦,但是通过C++string中的函数,我们可以先find,再replace:


class Solution {
public:
    string replaceSpace(string s) {
        size_t pos = s.find(' ');
        while(pos != string::npos)
        {
            s.replace(pos, 1, "%20");
            pos = s.find(' ',pos+3);//+3是优化
        }
        return s;
    }
};

+3实际上就是对代码的优化,因为%20就对应了3个位置,因此+3跳过这三个字符查找的效率更快。


解法2: 我们还可以新建一个string的空字符串,遍历传入的string s如果没有碰到空格就+=该字符,碰到了空格就+=%20


class Solution {
public:
    string replaceSpace(string s) {
        string ret;
        ret.reserve(s.size()*3);// 优化,提前开好空间,省去后续扩容的消耗
        for(auto ch : s)
        {
            if(ch != ' ')
            {
                ret += ch;
            }
            else
            {
                ret += "%20";
            }
        }
        return ret;
    }
};


ret.reserve(s.size()*3);是面对全是空格的情况,这样的极端情况就可以作为扩容的标准。这样我们发现,以空间换时间的做法就可以有效的提高效率。


3.3通过find取后缀


如果我们想确定一个文件是什么类型,我们需要知道其后缀,最后一个.后面的名字就是其后缀,那我们就可以先通过rfind()找到最后一个.的位置,再通过substr拷贝下来,这样就获得了相应文件的后缀了。(找最后一个.是由于在Linux下的文件可能存在类似于test.cpp.zip.tar的文件名,而其文件类型其实是tar)

微信图片_20230225133703.png


void test_string11()
{
  string file;
  cin >> file;
  size_t pos = file.rfind('.');
  if (pos != string::npos)
  {
    string suffix = file.substr(pos);
    cout << suffix << endl;
  }
}


3.4 getline的应用


将getline放在这里是因为这里直接利用会更具体的描述。题目:


HJ1 字符串最后一个单词的长度


这道题如果我们直接用scanf或者cin的话,对于输入hello nowcoder来说,实际上输入的只有hello,因为到空格就截止了,因此这里就需要了getline输入,getline的作用是到换行符时才结束输入。那我们看看代码实现:

#include <iostream>
#include<string>
using namespace std;
int main()
{
    string s;
    getline(cin , s);
    size_t pos = s.rfind(' ');
    cout << s.size() - pos - 1 <<endl;
    return 0;
}


4. string总结


本文详细介绍了string中各个函数功能以及接口,通过这些的灵活运用,才能更好的掌握C++,而对于这些函数的底层实现,下一篇将用的模拟实现string加深对各种函数的理解。

相关文章
|
8月前
|
存储 安全 C语言
C++ String揭秘:写高效代码的关键
在C++编程中,字符串操作是不可避免的一部分。从简单的字符串拼接到复杂的文本处理,C++的string类为开发者提供了一种更高效、灵活且安全的方式来管理和操作字符串。本文将从基础操作入手,逐步揭开C++ string类的奥秘,帮助你深入理解其内部机制,并学会如何在实际开发中充分发挥其性能和优势。
|
8月前
|
编译器 C++ 开发者
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
|
4月前
|
对象存储 C++ 容器
c++的string一键介绍
这篇文章旨在帮助读者回忆如何使用string,并提醒注意事项。它不是一篇详细的功能介绍,而是一篇润色文章。先展示重载函数,如果该函数一笔不可带过,就先展示英文原档(附带翻译),最后展示代码实现与举例可以直接去看英文文档,也可以看本篇文章,但是更建议去看英文原档。那么废话少说直接开始进行挨个介绍。
88 3
|
4月前
|
人工智能 机器人 编译器
c++模板初阶----函数模板与类模板
class 类模板名private://类内成员声明class Apublic:A(T val):a(val){}private:T a;return 0;运行结果:注意:类模板中的成员函数若是放在类外定义时,需要加模板参数列表。return 0;
94 0
|
4月前
|
存储 编译器 C语言
关于string的‘\0‘与string,vector构造特点,反迭代器与迭代器类等的讨论
你真的了解string的'\0'么?你知道创建一个string a("abcddddddddddddddddddddddddd", 16);这样的string对象要创建多少个对象么?你知道string与vector进行扩容时进行了怎么的操作么?你知道怎么求Vector 最大 最小值 索引 位置么?
82 0
|
4月前
|
存储 编译器 程序员
c++的类(附含explicit关键字,友元,内部类)
本文介绍了C++中类的核心概念与用法,涵盖封装、继承、多态三大特性。重点讲解了类的定义(`class`与`struct`)、访问限定符(`private`、`public`、`protected`)、类的作用域及成员函数的声明与定义分离。同时深入探讨了类的大小计算、`this`指针、默认成员函数(构造函数、析构函数、拷贝构造、赋值重载)以及运算符重载等内容。 文章还详细分析了`explicit`关键字的作用、静态成员(变量与函数)、友元(友元函数与友元类)的概念及其使用场景,并简要介绍了内部类的特性。
172 0
|
6月前
|
编译器 C++ 容器
【c++11】c++11新特性(上)(列表初始化、右值引用和移动语义、类的新默认成员函数、lambda表达式)
C++11为C++带来了革命性变化,引入了列表初始化、右值引用、移动语义、类的新默认成员函数和lambda表达式等特性。列表初始化统一了对象初始化方式,initializer_list简化了容器多元素初始化;右值引用和移动语义优化了资源管理,减少拷贝开销;类新增移动构造和移动赋值函数提升性能;lambda表达式提供匿名函数对象,增强代码简洁性和灵活性。这些特性共同推动了现代C++编程的发展,提升了开发效率与程序性能。
183 12
|
7月前
|
设计模式 安全 C++
【C++进阶】特殊类设计 && 单例模式
通过对特殊类设计和单例模式的深入探讨,我们可以更好地设计和实现复杂的C++程序。特殊类设计提高了代码的安全性和可维护性,而单例模式则确保类的唯一实例性和全局访问性。理解并掌握这些高级设计技巧,对于提升C++编程水平至关重要。
131 16
|
7月前
|
缓存 安全 Java
《从头开始学java,一天一个知识点》之:字符串处理:String类的核心API
🌱 **《字符串处理:String类的核心API》一分钟速通!** 本文快速介绍Java中String类的3个高频API:`substring`、`indexOf`和`split`,并通过代码示例展示其用法。重点提示:`substring`的结束索引不包含该位置,`split`支持正则表达式。进一步探讨了String不可变性的高效设计原理及企业级编码规范,如避免使用`new String()`、拼接时使用`StringBuilder`等。最后通过互动解密游戏帮助读者巩固知识。 (上一篇:《多维数组与常见操作》 | 下一篇预告:《输入与输出:Scanner与System类》)
153 11
|
8月前
|
编译器 C语言 C++
类和对象的简述(c++篇)
类和对象的简述(c++篇)