【编码艺术:掌握String类函数接口的妙用指南】(三)

简介: 【编码艺术:掌握String类函数接口的妙用指南】

【编码艺术:掌握String类函数接口的妙用指南】(二):https://developer.aliyun.com/article/1425650


5.6.clear()只是将string中有效字符清空,不改变底层空间大小。



6. string类对象的增删查改操作



6.1.增加


       在string尾部追加字符时,s.push_back(c) / s.append(1, c) / s += 'c'三种的实现方式差不多,一般情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可以连接字符串。


这里有一个问题:string s1 = "hello world";//这个可行吗?可行,这里是一个单参数的构造函数,它会隐式类型转换,它先去构造,然后再拷贝构造,不过这里编译器可能会优化成直接构造。


我们来看一下append的使用。

int main()
{
  std::string str;
  std::string str2 = "Writing ";
  std::string str3 = "print 10 and then 5 more";
  // used in the same order as described above:
  str.append(str2);                       // "Writing "
  str.append(str3, 6, 3);                   // "10 "
    str.append("here: ");                   // "here: "
  str.append("dots are cool", 5);          // "dots "
  str.append(10u, '.');                    // ".........."
  str.append(str3.begin() + 8, str3.end());  // " and then 5 more"
  std::cout << str << '\n';
  return 0;
}


我们上面有了尾插,那我们有没有头插呢?或者在中间插入呢?

// inserting into a string
#include <iostream>
#include <string>
int main ()
{
  std::string str="to be question";
  std::string str2="the ";
  std::string str3="or not to be";
  std::string::iterator it;
  // used in the same order as described above:
  str.insert(6,str2);                 // to be (the )question
  str.insert(6,str3,3,4);             // to be (not )the question
  str.insert(10,"that is cool",8);    // to be not (that is )the question
  str.insert(10,"to be ");            // to be not (to be )that is the question
  str.insert(15,1,':');               // to be not to be(:) that is the question
  it = str.insert(str.begin()+5,','); // to be(,) not to be: that is the question
  str.insert (str.end(),3,'.');       // to be, not to be: that is the question(...)
  str.insert (it+2,str3.begin(),str3.begin()+3); // (or )
  std::cout << str << '\n';
  return 0;
}


实际中头插使用较少,因为要挪动数据,效率不高。那删除字符呢?


6.2.删除


// string::erase
#include <iostream>
#include <string>
int main ()
{
  std::string str ("This is an example sentence.");
  std::cout << str << '\n';
                                           // "This is an example sentence."
  str.erase (10,8);                        //            ^^^^^^^^
  std::cout << str << '\n';
                                           // "This is an sentence."
  str.erase (str.begin()+9);               //           ^
  std::cout << str << '\n';
                                           // "This is a sentence."
  str.erase (str.begin()+5, str.end()-9);  //       ^^^^^
  std::cout << str << '\n';
                                           // "This sentence."
  return 0;
}


6.3.查找


注意:

  • pos含义:搜索字符串中要查找的第一个字符的位置。
  • 函数返回值:第一个匹配项的第一个字符的位置。 如果未找到任何匹配项,则该函数返回 string::npos

// string::find
#include <iostream>       // std::cout
#include <string>         // std::string
int main ()
{
  std::string str ("There are two needles in this haystack with needles.");
  std::string str2 ("needle");
  // different member versions of find in the same order as above:
  std::size_t found = str.find(str2);
  if (found!=std::string::npos)
    std::cout << "first 'needle' found at: " << found << '\n';
  found=str.find("needles are small",found+1,6);
  if (found!=std::string::npos)
    std::cout << "second 'needle' found at: " << found << '\n';
  found=str.find("haystack");
  if (found!=std::string::npos)
    std::cout << "'haystack' also found at: " << found << '\n';
  found=str.find('.');
  if (found!=std::string::npos)
    std::cout << "Period found at: " << found << '\n';
  // let's replace the first needle:
  str.replace(str.find(str2),str2.length(),"preposition");
  std::cout << str << '\n';
  return 0;
}


现在我们来使用一下find,查找字符串的后缀是什么?这里我们会使用到substr。

int main()
{
  string s1("Test.cpp");
  string s2("Test.tar.zip");
  size_t pos1 = s1.find('.');
  if(pos1 != string::npos)
  {
    string suff1 = s1.substr(pos1, s1.size() - pos1);
    cout << suff1 << endl;//.cpp
    string suff2 = s1.substr(pos1);//默认取到结尾
    cout << suff2 << endl;//.cpp
  }
  size_t pos2 = s2.find('.');
  if (pos2 != string::npos)
  {
    string suff1 = s2.substr(pos2, s2.size() - pos2);
    cout << suff1 << endl;//.tar.zip
    string suff2 = s2.substr(pos2);//默认取到结尾
    cout << suff2 << endl;//.tar.zip
  }
  //搜索字符串中由参数指定的序列的最后一次出现
  size_t pos3 = s2.rfind('.');
  if (pos2 != string::npos)
  {
    string suff1 = s2.substr(pos3, s2.size() - pos3);
    cout << suff1 << endl;//.zip
    string suff2 = s2.substr(pos3);//默认取到结尾
    cout << suff2 << endl;//.zip
  }
  return 0;
}


运行结果:


如何将一个网址分开呢?

#include <iostream>
#include <string>
using namespace std;
int main()
{
  string str("https://cplusplus.com/reference/string/string/substr/");
  string sub1, sub2, sub3;
  size_t pos1 = str.find(':');
  //左闭右开区间
  sub1 = str.substr(0, pos1 - 0);
  cout << sub1 << endl;
  size_t pos2 = str.find('/', pos1 + 3);
  sub2 = str.substr(pos1 + 3, pos2 - (pos1 + 3));
  cout << sub2 << endl;
  sub3 = str.substr(pos2 + 1);
  cout << sub3 << endl;
  return 0;
}


运行结果:


6.4.修改


// replacing in a string
#include <iostream>
#include <string>
int main ()
{
  std::string base="this is a test string.";
  std::string str2="n example";
  std::string str3="sample phrase";
  std::string str4="useful.";
  // replace signatures used in the same order as described above:
  // Using positions:                 0123456789*123456789*12345
  std::string str=base;           // "this is a test string."
  str.replace(9,5,str2);          // "this is an example string." (1)
  str.replace(19,6,str3,7,6);     // "this is an example phrase." (2)
  str.replace(8,10,"just a");     // "this is just a phrase."     (3)
  str.replace(8,6,"a shorty",7);  // "this is a short phrase."    (4)
  str.replace(22,1,3,'!');        // "this is a short phrase!!!"  (5)
  // Using iterators:                                               0123456789*123456789*
  str.replace(str.begin(),str.end()-3,str3);                    // "sample phrase!!!"      (1)
  str.replace(str.begin(),str.begin()+6,"replace");             // "replace phrase!!!"     (3)
  str.replace(str.begin()+8,str.begin()+14,"is coolness",7);    // "replace is cool!!!"    (4)
  str.replace(str.begin()+12,str.end()-4,4,'o');                // "replace is cooool!!!"  (5)
  str.replace(str.begin()+11,str.end(),str4.begin(),str4.end());// "replace is useful."    (6)
  std::cout << str << '\n';
  return 0;
}


根据上面的查找和修改可以轻松解决一个我们曾经遇到的问题,将日期"2023-11-23"中的'-'修改为'/'。

int main()
{
  string str("2023-11-23");
  cout << str << endl;
  size_t pos = str.find('-');
  // '-'修改为'/'
  while (pos != string::npos)
  {
    str.replace(pos, 1, 1, '/');
    pos = str.find('-');
  }
  cout << str << endl;
  return 0;
}


我们看一下我们的代码有什么问题没?我们发现我们每次找'-'都是从字符串的其实位置开始找,那么这也效率比较低,其实我们第一找到'-'后,得到第一次出现'-'的位置,后面再找'-'就可以从上次找的位置+1即可,这样效率就提高很多,但是replace的效率比较低,我们下面替换一个字符还好,但是如果替换成'///'时,此时就要往后挪动数据才能插入,这样效率比较低,所以replace我们能少用尽量就少用。

int main()
{
  string str("2023-11-23");
  cout << str << endl;
  size_t pos = str.find('-', 0);
  // '-'修改为'/'
  if (pos != string::npos)
  {
    str.replace(pos, 1, 1, '/');
    pos = str.find('-', pos + 1);
  }
  cout << str << endl;
  return 0;
}

所以这里我们有更好的方法,使用范围for+赋值。

int main()
{
  string str("2023-11-23");
  cout << str << endl;
  string str1;
  for (auto ch : str)
  {
    if (ch == '-')
      str1 += '/';
    else
      str1 += ch;
  }
  cout << str1 << endl;
    str.swap(str1);
  return 0;
}


运行结果:


这里有一个细节问题:我们上面使用的是C++ 标准库中 std::string 类的成员函数。


string::swap 是 C++ 标准库中 std::string 类的成员函数,用于交换两个字符串的内容。它是在字符串对象上调用的函数,例如:

std::string str1 = "Hello";
std::string str2 = "World";
str1.swap(str2); // 交换 str1 和 str2 的内容


swap 是一个通用的 C++ 函数,用于交换两个对象的值。对于字符串来说,可以使用 std::swap 或直接使用 swap 来交换两个字符串的内容,例如:

std::string str1 = "Hello";
std::string str2 = "World";
std::swap(str1, str2); // 交换 str1 和 str2 的内容
// 或者直接使用 swap
swap(str1, str2);


主要区别在于调用方式和命名空间。string::swap 是 std::string 类的成员函数,而 swap 是一个通用的函数,可以在合适的作用域下直接使用或通过 std::swap 来调用。两者都可以用于交换字符串的内容。同时std::swap 参数是函数模板,使用的时候实例化,需要拷贝,消耗较大,而string::swap 是直接交换两个字符串的地址,后面实现string类的时候可以看到。


【编码艺术:掌握String类函数接口的妙用指南】(四):https://developer.aliyun.com/article/1425656

相关文章
|
20天前
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
30 2
|
2月前
|
Java
【编程基础知识】(讲解+示例实战)方法参数的传递机制(值传递及地址传递)以及String类的对象的不可变性
本文深入探讨了Java中方法参数的传递机制,包括值传递和引用传递的区别,以及String类对象的不可变性。通过详细讲解和示例代码,帮助读者理解参数传递的内部原理,并掌握在实际编程中正确处理参数传递的方法。关键词:Java, 方法参数传递, 值传递, 引用传递, String不可变性。
65 1
【编程基础知识】(讲解+示例实战)方法参数的传递机制(值传递及地址传递)以及String类的对象的不可变性
|
2月前
|
安全 Java 测试技术
Java零基础-StringBuffer 类详解
【10月更文挑战第9天】Java零基础教学篇,手把手实践教学!
45 2
|
2月前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
25 1
|
2月前
|
数据可视化 Java
让星星月亮告诉你,通过反射创建类的实例对象,并通过Unsafe theUnsafe来修改实例对象的私有的String类型的成员属性的值
本文介绍了如何使用 Unsafe 类通过反射机制修改对象的私有属性值。主要包括: 1. 获取 Unsafe 的 theUnsafe 属性:通过反射获取 Unsafe类的私有静态属性theUnsafe,并放开其访问权限,以便后续操作 2. 利用反射创建 User 类的实例对象:通过反射创建User类的实例对象,并定义预期值 3. 利用反射获取实例对象的name属性并修改:通过反射获取 User类实例对象的私有属性name,使用 Unsafe`的compareAndSwapObject方法直接在内存地址上修改属性值 核心代码展示了详细的步骤和逻辑,确保了对私有属性的修改不受 JVM 访问权限的限制
60 4
|
2月前
|
存储 安全 Java
【一步一步了解Java系列】:认识String类
【一步一步了解Java系列】:认识String类
28 2
|
2月前
|
C语言 C++
C++番外篇——string类的实现
C++番外篇——string类的实现
21 0
|
3月前
|
Java 索引
java基础(13)String类
本文介绍了Java中String类的多种操作方法,包括字符串拼接、获取长度、去除空格、替换、截取、分割、比较和查找字符等。
44 0
java基础(13)String类
|
3月前
|
安全 Java
String类-知识回顾①
这篇文章回顾了Java中String类的相关知识点,包括`==`操作符和`equals()`方法的区别、String类对象的不可变性及其好处、String常量池的概念,以及String对象的加法操作。文章通过代码示例详细解释了这些概念,并探讨了使用String常量池时的一些行为。
String类-知识回顾①
|
2月前
|
安全 C语言 C++
【C++篇】探寻C++ STL之美:从string类的基础到高级操作的全面解析
【C++篇】探寻C++ STL之美:从string类的基础到高级操作的全面解析
40 4