10.3.4 string的武器库
C++标准库基本将string当成一个“字符容器”,然后在其上可套用多种通用算法,造成字符串算法严重不足。
boost又一次承担了救火员的角色,提供了丰富的字符串算法,并且同时支持string和wstring。
这些扩展字符串算法,在<boost/algorithm/string.hpp>文件中声明,它们并不仅仅适用于string或wstring,也适用于其他一些容器,但这里仍然简单称它们为“string_algo”.
string_algo中的许多算符都有多个版本,通过名字上的修饰字符不同,可做如下识别:
(1)前缀 i: 表示该算法不区分大小写;
(2)后缀 _copy: 表示该算法将复制一份入参,再对复制品修改,不影响原入参;
(3)后缀 _if: 表示该算法将提供一个函数对象,仅当该条件满足才处理过程数据。
和字符串最常相关的处理,无非是:大小写转换,去空白符,分类判断,查找,替换,删除,分割合并。
1.大小写转换
//直接转换成大写
void to_upper(T& input);
//直接转换成小写
void to_lower(T& input);
//将复制品转换成大写并返回
T to_upper_copy(T const& input);
//将复制品转换成小写并返回
T to_lower_copy(T const& input);
这里的T代表std::string或std::wstring甚至其他类型,因此实际实现的都是模板,为了描述方便,我们将它们写成函数的形式。
...
#include <string>
#include <boost/algorithm/string.hpp>
...
namespace str_algo = boost::algorithm;
...
void test_upper_lower()
{
string s = str_algo::to_upper_copy(string("hello"));
cout << s << std::endl;
str_algo::to_lower(s);
cout << s << std::endl;
}
2.去空白符
当从文件,网络,数据库读入字符串时,往往需要去除其两端的空白字符:
//去除Input左边的空白符
void trim_left(T& Input);
//去除Input右边的空白符
void trim_right(T& Input);
//去除Input两边的空白符
void trim(T& Input);
trim函数同样都已_copy版本,用于在得到去除空白符之后的复制品。
trim函数还提供了_if 版本,可用于去除特定的字符,比如有一个字符串内容为:"123ABC00DE456",若要将其改变成“ABC00DE”,可以他提供一个函数对象,作为判断式传入
bool is_digit(char c)
{
return (c >= '0' && c <= '9');
}
void test_trim()
{
string s = " ABC DEF ";
str_algo::trim(s);
cout << s << endl;
s = "123ABC00DE456";
string s2 = str_algo::trim_copy_if(s, is_digit);
cout << s2 << endl;
}
3.分类判断
前面我们动手写了一个用于判断给定字符是否为数字的判断式:is_digit。所谓“判断式(predicate)”,通常就是一个函数或函数对象,返回值是bool值,而入参则是调用者所要求提供判断的数据,在string_algo中,它是广义上的“字符”类型。
string_algo提供的常用字符分类判断如表10-3所列。
分类判断式 | 含义 | 备注 |
is_space | 是否空白字符 | 至少包括 ' ' '\t' '\r' '\n' |
is_alnum | 是否字母或数字 | |
is_alpha | 是否字母 | |
is_punct | 是否标点符号 | |
is_lower | 是否小写字母 | |
is_upper | 是否大写字母 | |
is_print | 是否可打印字符 | |
is_digit | 是否数字(十进制) | |
is_xdigit | 是否数字(十六进制) | |
is_any_of | 是否是指定字符串中的某个字符 | |
is_form_range | 是否指定范围内的字符 | 需一对入参表示范围:[from, to] |
注意,这些函数若要支持汉字(比如汉字标点符号),需在操作系统环境支持,
mingw下由于没有实现汉字环境的的本地化(locate),所以可知其明确不支持汉字。
下面的调用代码编译不了:
boost::algorithm::is_space('\n');
因为要判断一个字母是不是空白符数字,不是那么简单,我们通常只处理英美的字符,但在法语和德语中,它们的字母表是什么?这就需要前面的本地化工作。
is_space()等函数,事实上有一个默认的locate参数,用于传递各国的本地化处理,中国字符由于涉及宽字符等,所以mingw环境下没有实现,但欧洲主要语言的字符的类型判断,确实可以通过本国locate参数实现,如果什么都不传,那么默认其就是纯C的环境,基本可认为就是英美字符体系。
接着,is_space(const std::locale& Loc = std::locale())等函数,事实上创建了另外一个对象(正好也是一个函数对象),然后再调用那个临时对象的括号重载操作;
boost::algorithm::is_space()('\n');//编译通过
也有部分判断式不需要locate信息,但它们采用类似的设计,即通过入参先生成一个中间临时对象,然后再调用临时对象的括号操作符重载函数。比如,is_from_range需要这样调用:
//判断b是否在'A'和'z'之间
boost::algorithm::is_from_range('A', 'z')('b');
而 is_any_of 这么调用:
//判断'B'是不是"ABCDEFG"中的某个字符
cout << str_algo::is_any_of("ABCDEFG")('B');