22.1 — std::string 和 std::wstring

标准库包含许多有用的类——但或许最有用的是 std::string。std::string(和 std::wstring)是一个字符串类,它提供了许多操作来赋值、比较和修改字符串。在本章中,我们将深入研究这些字符串类。

注意:C 风格字符串将被称为“C 风格字符串”,而 std::string(和 std::wstring)将简称为“字符串”。

作者注

本章有些过时,可能会在未来的更新中进行精简。您可以随意浏览材料以获取想法和有用的示例,但建议优先使用技术参考网站(例如 cppreference)以获取最新信息。

字符串类的动机

在之前的课程中,我们介绍了C 风格字符串,它使用字符数组来存储字符串。如果您尝试使用 C 风格字符串做任何事情,您会很快得出结论,它们难以处理,容易出错,并且难以调试。

C 风格字符串有许多缺点,主要围绕着您必须自己进行所有内存管理这一事实。例如,如果您想将字符串“hello!”赋值到一个缓冲区中,您必须首先动态分配一个正确长度的缓冲区

char* strHello { new char[7] };

不要忘记为 null 终止符多算一个字符!

然后您必须实际复制值

strcpy(strHello, "hello!");

希望您的缓冲区足够大,这样就不会发生缓冲区溢出!

当然,由于字符串是动态分配的,因此您必须记住在使用完毕后正确地释放它

delete[] strHello;

不要忘记使用数组删除而不是普通删除!

此外,C 提供了许多直观的运算符来处理数字,例如赋值和比较,但它们根本不适用于 C 风格字符串。有时这些会看起来有效,但实际上会产生不正确的结果——例如,使用 == 比较两个 C 风格字符串实际上会进行指针比较,而不是字符串比较。使用 operator= 将一个 C 风格字符串赋值给另一个 C 风格字符串最初看起来会有效,但实际上是进行指针复制(浅拷贝),这通常不是您想要的。这些情况可能导致程序崩溃,而且很难找到和调试!

总之,使用 C 风格字符串需要记住许多关于安全/不安全的繁琐规则,记住一堆名称奇怪的函数,如 strcat() 和 strcmp(),而不是使用直观的运算符,以及进行大量的手动内存管理。

幸运的是,C++ 和标准库提供了一种更好的处理字符串的方法:std::string 和 std::wstring 类。通过利用 C++ 的概念,如构造函数、析构函数和运算符重载,std::string 允许您以直观和安全的方式创建和操作字符串!不再需要内存管理,不再有奇怪的函数名称,并且大大降低了发生灾难的可能性。

请注册我!

字符串概述

标准库中的所有字符串功能都位于头文件中。要使用它,只需包含字符串头

    #include <string>

字符串头文件中实际上有 3 个不同的字符串类。第一个是名为 basic_string<> 的模板基类

namespace std
{
    template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT> >
        class basic_string;
}

您不会直接使用此课程,因此暂时无需担心 traits 或 Allocator 的作用。在几乎所有可想象的情况下,默认值就足够了。

标准库提供了两种 basic_string<> 的变体

namespace std
{
    typedef basic_string<char> string;
    typedef basic_string<wchar_t> wstring;
}

这些是您实际将使用的两个类。std::string 用于标准 ascii 和 utf-8 字符串。std::wstring 用于宽字符/unicode (utf-16) 字符串。没有内置的 utf-32 字符串类(尽管如果需要,您应该能够从 basic_string<> 扩展自己的)。

虽然您将直接使用 std::string 和 std::wstring,但所有字符串功能都在 basic_string<> 类中实现。String 和 wstring 能够通过模板化的特性直接访问该功能。因此,所有呈现的函数都将适用于 string 和 wstring。但是,由于 basic_string 是一个模板类,这也意味着当您对 string 或 wstring 进行语法不正确的操作时,编译器将产生可怕的模板错误。不要被这些错误吓倒;它们看起来比实际情况糟得多!

以下是字符串类中所有函数的列表。这些函数中的大多数都有多种形式来处理不同类型的输入,我们将在下一课中更深入地介绍。

函数效果
创建和销毁
(构造函数)
(析构函数)
创建或复制字符串
销毁字符串
大小和容量
capacity()
empty()
length(), size()
max_size()
reserve()
返回在不重新分配的情况下可以容纳的字符数
返回一个布尔值,指示字符串是否为空
返回字符串中的字符数
返回可以分配的最大字符串大小
扩展或缩小字符串的容量
元素访问
[], at()访问特定索引处的字符
修改
=, assign()
+=, append(), push_back()
insert()
clear()
erase()
replace()
resize()
swap()
为字符串分配新值
将字符连接到字符串末尾
在字符串中的任意索引处插入字符
删除字符串中的所有字符
删除字符串中任意索引处的字符
将任意索引处的字符替换为其他字符
扩展或缩小字符串(截断或在字符串末尾添加字符)
交换两个字符串的值
输入输出
>>, getline()
<<
c_str()
copy()
data()
从输入流中读取值到字符串中
将字符串值写入输出流
将字符串内容作为以 NULL 结尾的 C 风格字符串返回
将内容(非 NULL 结尾)复制到字符数组
与 c_str() 相同。非 const 重载允许写入返回的字符串。
字符串比较
==, !=
<, <=, > >=
compare()
比较两个字符串是否相等/不相等(返回 bool)
比较两个字符串是小于还是大于彼此(返回 bool)
比较两个字符串是否相等/不相等(返回 -1, 0 或 1)
子字符串和连接
+
substr()
连接两个字符串
返回一个子字符串
搜索
find()
find_first_of()
find_first_not_of()
find_last_of()
find_last_not_of()
rfind()
查找第一个字符/子字符串的索引
查找字符集中第一个字符的索引
查找字符集中第一个不属于该字符集的字符的索引
查找字符集中最后一个字符的索引
查找字符集中最后一个不属于该字符集的字符的索引
查找最后一个字符/子字符串的索引

迭代器和分配器支持
begin(), end()
get_allocator()
rbegin(), rend()
字符串开头/结尾的正向迭代器支持
返回分配器
字符串开头/结尾的反向迭代器支持

虽然标准库字符串类提供了许多功能,但仍有一些显著的遗漏

  • 从数字创建字符串的构造函数
  • 大小写转换/大写/小写函数
  • 不区分大小写的比较
  • 分词/将字符串拆分为数组
  • 轻松获取字符串左侧或右侧部分的函数
  • 空白字符修剪
  • sprintf 风格的字符串格式化
  • utf-8 到 utf-16 或反向转换

对于其中大多数,您必须要么自己编写函数,要么将字符串转换为 C 风格字符串(使用 c_str())并使用提供此功能的 C 函数。

在接下来的课程中,我们将更深入地研究字符串类的各种函数。尽管我们将使用字符串作为示例,但所有内容同样适用于 wstring。

guest
您的电子邮箱地址将不会被显示
发现错误?请在上方留言!
与勘误相关的评论在处理后将被删除,以帮助减少混乱。感谢您帮助使网站对每个人都更好!
来自 https://gravatar.com/ 的头像与您提供的电子邮箱地址相关联。
有回复时通知我:  
107 评论
最新
最早 最多投票
内联反馈
查看所有评论