购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

2.8 创建字符串辅助库

标准库的字符串类型是一种通用的实现,它缺乏许多有用的方法,比如更改大小写、裁剪、分割和其他可能满足不同开发人员需求的方法。虽然存在提供丰富字符串功能的第三方库,然而,在本节中,我们将着眼于实现几个简单但有用的方法,这些方法在实践中可能经常用到。其目的是了解如何使用字符串方法和标准通用算法来操作字符串,同时还可以提供可在应用程序中使用的可重用代码参考。

在本节中,我们将实现一个字符串小工具库,它将提供执行以下操作的函数:

❍ 将字符串更改为小写或大写。

❍ 反转字符串。

❍ 删除字符串开头/结尾的空格。

❍ 从字符串的开头/结尾删除一组特定的字符。

❍ 删除字符串中任意地方的字符。

❍ 使用特定的分隔符标记字符串。

在开始实现这个工具库之前,我们先看一些先决条件。

2.8.1 准备工作

我们将要实现的字符串库应该可以处理所有标准字符串类型;std::string、std::wstring、std::u16string和std::u32string。

为避免指定长名称,如std::basic_string<CharT,std::char_traits<CharT>,std::allocator<CharT>>,我们将为字符串和字符串流使用以下模板别名:

要实现这些字符串辅助函数,我们需要包含头文件<string>。同理,如果要使用通用标准算法,那么还需要包含<algorithm>头文件。

在本节的所有示例中,我们将对C++14中的字符串使用标准的用户自定义字面量操作符,为此我们需要显式地使用std::string_literals命名空间。

2.8.2 使用方式

❍ 要将字符串转换为小写或大写,请使用通用算法std::transform()对字符串中的字符应用tolower()或toupper()函数:

❍ 要反转字符串,请使用通用算法std::reverse():

❍ 要删除字符串开头、结尾的空格字符或同时删除这两处的空格字符,请使用std::basic_string方法find_first_not_of()和find_last_not_of():

❍ 如果要从字符串中删除给定的字符串子集,请使用std::basic_string方法find_first_not_of()和find_last_not_of()的重载版本,它们接受一个定义要查找的字符串子集的字符串参数:

❍ 要删除字符串中的字符,请使用std::remove_if()和std::basic_string::erase():

❍ 要基于给定的分隔符分割字符串,首先使用std::getline()读取被字符串内容初始化的std::basic_stringstream变量,然后从中提取分割后的字符串,最后将它们放到字符串vector中。

2.8.3 工作原理

要实现库中的实用函数,我们有两个选择:

❍ 函数会修改通过引用参数传递的字符串。

❍ 函数不会改变原来的字符串,而是返回一个新的字符串。

第二个选择的优点是它保留了原始字符串,这在许多情况下可能很有意义。否则,在这些情况下,你首先必须创建字符串的副本,然后更改副本。本节中提供的实现采用了第二种方法。

我们在2.8.2节实现的第一类函数是to_upper()和to_lower(),这些函数将字符串的内容转换为大写或小写,实现这一点的最简单方法是使用标准算法std::transform()。这是一种通用算法,它将函数应用于由begin和end迭代器定义的范围内的每个元素,并将结果存储在只需要指定begin迭代器的范围中。输出范围可以与输入范围相同,这正是我们在转换字符串时所做的。我们用标准库函数toupper()或tolower()实现:

我们需要考虑的下一个函数是reverse(),就如字面上的意思,reverse()函数将字符串的内容反转。为此,我们使用std::reverse()标准算法,这个通用算法反转由begin和end迭代器定义的范围内的元素:

当涉及删除空格字符时,可以在字符串开头、结尾或者首尾同时删除。因此,我们实现了三个不同版本的函数:trim()用于删除字符串首尾的空格字符,trimleft()用于删除开头的空格字符,trimright()用于删除结尾的空格字符。该函数的第一个版本只删除空格。为了能够正确地找到需要删除的部分,我们使用std::basic_string的find_first_not_of()和find_last_not_of()方法。它们返回字符串中不属于指定字符的第一个和最后一个字符。随后,调用std::basic_string的substr()方法将返回一个新的字符串,substr()方法接受字符串中的一个索引和一些要复制到新字符串的元素:

有时,从字符串中删除其他字符和空格是有用的。为此,我们为指定要删除的一组字符的修剪函数提供了重载。它的实现与前一个非常相似,因为find_first_not_of()和find_last_not_of()都有重载,这些重载接受一个包含要在搜索中排除的字符的字符串:

如果必须从字符串的任意部分删除字符,则修剪方法将无能为力,因为它们只能处理字符串开头和结尾处的连续字符序列。为此,我们实现了一个简单的remove()方法,它使用std:remove_if()标准算法。

std::remove()和std::remove_if()的工作方式一开始可能不是很直观,它们通过重新排列第一个和最后一个迭代器定义的范围的内容,从该范围中删除满足条件的元素(用move赋值)。需要删除的元素被放在范围的末尾,函数返回一个迭代器,该迭代器指向被删除元素的范围中的第一个元素。这个迭代器基本上定义了修改后的范围的新end迭代器,如果没有元素需要删除,则返回的end迭代器就是原范围迭代器的end迭代器。然后使用这个返回的迭代器的值调用std::basic_string::erase()方法,该方法会删除由两个迭代器定义的字符串的内容。在我们的例子中,这两个迭代器是由std::remove_if()返回的迭代器和指向字符串结尾的end迭代器:

我们实现的最后一个方法split()根据指定的分隔符分割字符串的内容。有多种方法可以实现这一点。在这个实现中,我们使用std::getline()。这个函数从输入流中读取字符直到找到指定的分隔符,并将这些字符放入字符串中。在开始读取输入缓冲区之前,它调用erase()来清除输出字符串的内容。在循环中调用此方法会将分割后的字符串放到vector中。在我们的实现中,我们忽略分割后的字符串为空的结果:

这里展示了两个文本分割的示例。在第一个示例中,text5变量中的文本被分割成单词,并且如前所述,空字符串将被忽略;在第二个示例中,分割空字符串会产生空的vector。

2.8.4 延伸阅读

❍ 阅读2.5节,以了解如何创建用户自定义类型的字面量。

❍ 阅读1.2节,以了解类型别名。 OepKgXjh4DIugpzMy31s8/Ze4Dm3J4dCfDgMp6F83RBcVCk9XIyIBm3JpWyAo/qI

点击中间区域
呼出菜单
上一章
目录
下一章
×