引言
在c++中,std::string提供了许多字符串操作符函数,让我们能够秦松驾驭文本数据,而与此同时,非成员函数的重载更是为string类增添了别样的魅力,输入输出流的重载让我们像处理基本类型的数据一样方便地读取和输出字符串,连接操作符的重载使得字符串的拼接变得简洁直观。在这篇博客中,我们将一同深入剖析C++中string类的字符串操作符和非成员函数的重载,为大家在编程之旅中增添一份有力的武器。
在开始介绍操作符函数前,我们先来了解一个string类定义的一个静态成员变量npos。
1.npos
npos是std::string中定义的一个静态常量成员,表示“no opsition”(没有位置)。它通常用于返回表示查找操作失败时的返回值 。
static const size_t npos = -1;
#include <iostream>
using namespace std;
int main()
{
string s1 = "Hello, World";
size_t pos = s1.find("good");
if (pos == string::npos);
{
cout << "字符串未找到" << endl;
}
return 0;
}
2.string类的字符串操作函数
2.1. c_str()
c_str用来返回一个指向C风格字符串(以空字符\0结尾)的指针。适用于需要与C语言或库(如printf、文件操作等)进行交互时将std::string转换为C风格字符串
(1)函数原型
const char* c_str() const;
(2)示例代码
#include <iostream>
using namespace std;
int main()
{
string s1 = "Hello";
const char* cstr = s1.c_str();
cout << cstr << endl;//Hello
return 0;
}
(3)string与C风格字符串的区别
在 C++中, std::string 类型的字符串与 C 风格字符串有以下一些主要区别:
一、内存管理
- std::string :自动管理内存。当对 std::string 对象进行赋值、拼接等操作时,它会自动处理内存的分配和释放,无需程序员手动管理。
- C 风格字符串:需要手动管理内存。如果是动态分配的字符数组,程序员必须在合适的时候使用 free (C)或 delete[] (C++)释放内存,否则会导致内存泄漏。对于局部的字符数组,其内存会在作用域结束时自动释放,但长度在创建时就固定,难以灵活调整。
二、安全性
- std::string :通常具有边界检查机制,不容易出现缓冲区溢出等安全问题。例如,在进行字符串拼接时,会自动调整内存大小以容纳新的内容。
- C 风格字符串:缺乏内置的安全检查机制。在进行字符串操作时,如复制、连接等,如果不仔细控制长度,很容易导致缓冲区溢出,可能会破坏程序的稳定性甚至被恶意利用。
三、功能丰富度
- std::string :提供了丰富的成员函数,如 find (查找子串)、 substr (提取子串)、 append (追加字符串)等,使得字符串操作更加方便和直观。
- C 风格字符串:主要依靠 C 标准库中的函数,如 strcpy (复制字符串)、 strcat (连接字符串)、 strstr (查找子串)等,这些函数使用起来相对较为繁琐,且容易出错。
四、初始化和赋值
- std::string :可以使用多种方式进行初始化和赋值,例如使用字符串常量、另一个 std::string 对象、字符数组的一部分等。初始化和赋值操作通常比较简洁明了。
- C 风格字符串:初始化通常需要使用字符数组初始化语法或者使用 strcpy 等函数进行复制。赋值操作一般使用 strcpy 或 strncpy 等函数,但需要确保目标字符数组有足够的空间容纳源字符串。
五、可变性
- std::string :可以方便地修改其中的字符内容。例如,可以直接通过下标访问和修改单个字符。
- C 风格字符串:如果是字符数组常量(如 char str[] = "Hello"; ),存储在只读内存区域,不能修改其内容。如果是动态分配的字符数组,可以修改其内容,但需要小心操作以避免越界。
2.2 copy()
copy()用来将字符串的内容复制到字符串数组中,但不自动添加空字符‘\0’。
(1)函数原型
size_t copy (char* s, size_t len, size_t pos = 0) const;
- s:目标字符数组
- len:要复制的字符数量。
- pos:从字符串的哪个位置开始复制,默认为0.
- const:表示该函数不会修改调用它的字符串对象(即whis指针所指的对象)
(2)示例代码
#include <iostream>
using namespace std;
int main()
{
string s1 = "Hello, henu";
char s2[20];
s1.copy(s2, 5, 7);
s2[5] = '\0';
cout << s2 << endl;//henu
return 0;
}
2.3 substr()
substr()用来提取从指定位置开始的子字符串。
(1)函数原型
string substr (size_t pos = 0, size_t len = npos) const;
- pos:字符串起始位置,默认为0
- len:子字符串的长度,默认为字符串的剩余部分 。
- const:表示该函数不会修改调用它的字符串对象(即whis指针所指的对象)
(2)示例代码
#include <iostream>
using namespace std;
int main()
{
string s1 = "Hello";
string s2;
string s3;
s2 = s1.substr(1, 3);
s3 = s1.substr();
cout << s2 << endl;//ell
cout << s3 << endl;//Hello
return 0;
}
2.4 find()与rfind()
2.4.1 find()
- find()用来查找字符串中指定字符串第一次出现的位置。如果未找到,则返回std::string::npos,
- rfind()用于在字符串中查找最后一次出现指定字符串的位置。返回值是找到子串的起始位置如果未找到,则返回npos。
(1)函数原型
string (1)
size_t find (const string& str, size_t pos = 0) const noexcept;
c-string (2)
size_t find (const char* s, size_t pos = 0) const;
buffer (3)
size_t find (const char* s, size_t pos, size_type n) const;
character (4)
size_t find (char c, size_t pos = 0) const noexcept;
string (1)
size_t rfind (const string& str, size_t pos = npos) const;
c-string (2)
size_t rfind (const char* s, size_t pos = npos) const;
buffer (3)
size_t rfind (const char* s, size_t pos, size_t n) const;
character (4)
size_t rfind (char c, size_t pos = npos) const;
find从指定位置往后查找,rfind从指定位置往前查找
(2)深入解析
1.查找字符串
size_t find (const string& str, size_t pos = 0) const;
size_t rfind (const string& str, size_t pos = npos) const;
//pos = npos默认查找整个字符串
返回值是size_t,表示找到的位置索引。返回std::string::npos,这是一个特殊值,表示失败。
#include <iostream>
using namespace std;
int main()
{
string s1 = "Hello, World! World!";
string s2 = "World!";
size_t pos1 = s1.find(s2);//查找W第一次出现的位置
size_t pos2 = s1.rfind(s2);//查找W最后一次出现的位置
if (pos1 == string::npos)
{
cout << "没找到" << endl;
}
else
{
cout << "找到了, 索引是" << pos1 << endl;//输出7
}
if (pos2 == string::npos)
{
cout << "没找到" << endl;
}
else
{
cout << "找到了, 索引是" << pos2 << endl;//输出14
}
return 0;
}
2.查找c风格字符串
size_t find (const char* s, size_t pos = 0) const;
size_t rfind (const char* s, size_t pos = 0) const;
//pos = npos默认查找整个字符串
#include <iostream>
using namespace std;
int main()
{
string s1 = "Hello, World!World!";
size_t pos1 = s1.find("World");
size_t pos2 = s1.rfind("World");
if (pos1 == string::npos)
{
cout << "没找到" << endl;
}
else
{
cout << "找到了,索引是" << pos1 << endl;//7
}
if (pos2 == string::npos)
{
cout << "没找到" << endl;
}
else
{
cout << "找到了,索引是" << pos2 << endl;//13
}
return 0;
}
3.查找字符缓冲区
size_t find (const char* s, size_t pos, size_t n) const;
size_t rfind (const char* s, size_t pos, size_t n) const;
//pos = npos默认查找整个字符串
- s:指向要查找的字符缓冲区的指针,类型为const char*。
- pos:从字符串中的那个位置开始向前查找(可以是npos,表示从末尾查找)。
- n: 要查找的字符缓冲区的前n个字符。
#include <iostream>
using namespace std;
int main()
{
string s1 = "Hello, World!World!";
const char* but = "World!";//要查汇的字符缓冲区
size_t pos1 = s1.find(but, 0, 5);//从位置0开始向后查找缓冲区的前五个字符
size_t pos2 = s1.rfind(but, string::npos, 5);//从末尾开始向前查找缓冲区的前五个字符
if (pos1 == string::npos)
{
cout << "没找到" << endl;
}
else
{
cout << "找到了,索引是" << pos1 << endl;//7
}
if (pos2 == string::npos)
{
cout << "没找到" << endl;
}
else
{
cout << "找到了,索引是" << pos2 << endl;//13
}
return 0;
}
4.查找单个字符
size_t find (char c, size_t pos = 0) const;
size_t rfind (char c, size_t pos = 0) const;
//pos = npos默认查找整个字符串
#include <iostream>
using namespace std;
int main()
{
string s1 = "Hello, World!World!";
size_t pos1 = s1.find('W');
size_t pos2 = s1.rfind('W');
if (pos1 == string::npos)
{
cout << "没找到" << endl;
}
else
{
cout << "找到了,索引是" << pos1 << endl;//7
}
if (pos2 == string::npos)
{
cout << "没找到" << endl;
}
else
{
cout << "找到了,索引是" << pos2 << endl;//13
}
return 0;
}
2.6 其它成员函数
(1)data()
data用来返回指向存储字符串内容的字符数组的指针。与c_str的不同是返回的指针不是以 `\0` 结尾的。
const char* data() const noexcept;
#include <iostream>
using namespace std;
int main() {
string str = "Hello, World!";
// 使用 data() 获取字符串的字符数组
const char* ptr = str.data();
cout << ptr << endl;// 输出 "Hello, World!"
return 0;
}
(2)compare
compare用于比较两个字符串或子字符串的内容。
string (1)
int compare (const string& str) const noexcept;
substrings (2)
int compare (size_t pos, size_t len, const string& str) const;
int compare (size_t pos, size_t len, const string& str,
size_t subpos, size_t sublen) const;
c-string (3)
int compare (const char* s) const;
int compare (size_t pos, size_t len, const char* s) const;
buffer (4)
int compare (size_t pos, size_t len, const char* s, size_t n) const;
返回一个整数值,负值表示当前字符串小于str,0表示当前字符串等于str,正值表示当前字符串大于str。
#include <iostream>
using namespace std;
int main()
{
string str1("green apple");
string str2("red apple");
string str3("apple");
if (str1.compare(str2) != 0)//比较两个字符串
cout << str1 << " is not " << str2 << endl;
if (str1.compare(6, 5, str3) == 0)//索引6之后的五个字符串与s3相比
cout << str1 << " is an apple\n";
if (str2.compare(str2.size() - 5, 5, "apple") == 0)
cout << "and " << str2 << " is also an apple\n";
if (str1.compare(6, 5, str2, 4, 5) == 0)
//索引6之后的五个字符串与s2索引为4后的五个字符串相比
cout << "therefore, both are apples\n";
return 0;
}
(3)get_aooocator
std::string::get_allocator用于返回 std::allocator<char> 类型的对象。这个对象是用于管理字符串分配和释放内存的分配器。
allocator_type get_allocator() const noexcept;
#include <iostream>
using namespace std;
int main() {
string str = "Hello, World!";
allocator<char> alloc = str.get_allocator();
// 使用返回的 allocator 分配内存
char* buffer = alloc.allocate(20);
// 分配的内存可以用于存储数据
for (int i = 0; i < 12; ++i)
{
buffer[i] = str[i];
}
buffer[12] = '\0'; // 添加字符串终止符
cout << buffer << endl;
// 使用 allocator 释放内存
alloc.deallocate(buffer, 20);
return 0;
}
在这个示例中,get_allocator 返回的 allocator 对象被用来分配和释放字符数组的内存,这与字符串的内存管理方式相同。
(4)find_first_of与find_last_of
它用于在字符串中查找指定字符中任意一个字符的第一次出现的位置。
string (1)
size_t find_first_of (const string& str, size_t pos = 0) const noexcept;
c-string (2)
size_t find_first_of (const char* s, size_t pos = 0) const;
buffer (3)
size_t find_first_of (const char* s, size_t pos, size_t n) const;
character (4)
size_t find_first_of (char c, size_t pos = 0) const noexcept;
- pos:从字符串中开始查找的起始位置(默认为 0)。该函数返回找到的第一个字符的索引。如果没有找到任何指定的字符,则返回std::string::npos,这是一个常量,表示没有有效位置。
string (1)
size_t find_last_of (const string& str, size_t pos = npos) const noexcept;
c-string (2)
size_t find_last_of (const char* s, size_t pos = npos) const;
buffer (3)
size_t find_last_of (const char* s, size_t pos, size_t n) const;
character (4)
size_t find_last_of (char c, size_t pos = npos) const noexcept;
pos:从字符串的指定位置开始向前查找(默认为std::string::npos,表示从字符串的末尾开始查找)。该函数返回找到的第一个字符的索引。如果没有找到任何指定的字符,则返回std::string::npos,这是一个常量,表示没有有效位置。
#include <iostream>
using namespace std;
int main()
{
string str1 = "Hello, World";
string str2 = "World";
size_t pos1 = str1.find_first_of(str2);
size_t pos2 = str1.find_first_of("Wd");
size_t pos3 = str1.find_first_of("World", 0, 5);
size_t pos4 = str1.find_first_of('o');
cout << pos1 << endl;//2,str2中的'l'第一次出现在索引为2处
cout << pos2 << endl;//7,'Wd'中的'W'第一次出现在索引为7处
cout << pos3 << endl;//2
cout << pos4 << endl;//4
return 0;
}
#include <iostream>
using namespace std;
int main()
{
string str1 = "Hello, World";
string str2 = "World";
size_t pos1 = str1.find_last_of(str2);
size_t pos2 = str1.find_last_of("He");
size_t pos3 = str1.find_last_of("World", 0, 5);
size_t pos4 = str1.find_last_of('o');
cout << pos1 << endl;//11,str2中的'd'第一次出现在索引为11处
cout << pos2 << endl;//1,'He'中的'e'第一次出现在索引为1处
cout << pos3 << endl;//18446744073709551615没有找到
cout << pos4 << endl;//6
return 0;
}
(5)find_first_not_of与find_last_not_of
find_first_not_of用于查找字符串中第一个不属于指定字符集合的字符的位置(从前往后查找)。find_last_not_of用于查找字符串中最后一个不属于指定字符集合的字符的位置(从后往前查找)。
string (1)
size_t find_first_not_of (const string& str, size_t pos = 0) const noexcept;
c-string (2)
size_t find_first_not_of (const char* s, size_t pos = 0) const;
buffer (3)
size_t find_first_not_of (const char* s, size_t pos, size_t n) const;
character (4)
size_t find_first_not_of (char c, size_t pos = 0) const noexcept;
string (1)
size_t find_last_not_of (const string& str, size_t pos = npos) const noexcept;
c-string (2)
size_t find_last_not_of (const char* s, size_t pos = npos) const;
buffer (3)
size_t find_last_not_of (const char* s, size_t pos, size_t n) const;
character (4)
size_t find_last_not_of (char c, size_t pos = npos) const noexcept;
用法同find_first_of和find_last_of类似,不在赘述。
总结
总结:在 C++ 中,std::string 类提供了多种功能强大的成员函数,使字符串处理变得灵活、高效。通过这些操作函数,开发者能够对字符串进行查找、替换、获取子串、内存管理等多种操作,极大地提升了开发效率。以下是一些重要的操作函数总结:
- c_str:c_str() 和 data() 函数提供了字符串的 C 风格字符数组等效形式。这在需要与 C 风格字符串或其他库进行交互时非常实用。
- initializer list 替换:replace 函数可以使用初始化列表对字符串中的某个范围进行替换。这种操作在处理特定范围的修改时非常有用。
- copy:通过 copy 函数可以将字符串的一部分复制到字符数组中,适用于需要操作原始字符数据的场景。
- get_allocator:该函数返回用于管理 std::string 内存的分配器,便于自定义内存管理。
- 查找功能:
- find:查找字符串中某内容首次出现的位置。
- rfind:查找字符串中某内容最后一次出现的位置。
- find_first_of:查找指定字符集中的任意字符第一次出现的位置。
- find_last_of:从字符串末尾开始查找指定字符集中的任意字符。
- find_first_not_of:查找第一个不属于指定字符集的字符。
- find_last_not_of:从末尾开始查找不属于指定字符集的字符。
- 子串操作:substr 函数可以生成字符串的子串,适用于提取特定部分内容。
- 字符串比较:compare 函数提供了灵活的字符串比较功能,用于按字典顺序比较两个字符串。
这些操作函数使 std::string 成为一个强大的工具,能够处理从简单字符串操作到复杂的字符串匹配、替换等任务。掌握这些函数可以帮助开发者编写出高效、健壮的字符串处理代码。在实际开发中,这些函数的组合应用能够显著提高代码的灵活性和可读性。
更多string成员函数:string::find - C++ Reference (cplusplus.com)