第 5 章 字符串处理
5.1. 前言
在标准 C++ 中,用于处理字符串的是 std::string
类,它提供很多字符串操作,包括查找指定字符或子串的函数。 尽管 std::string
囊括了百余函数,是标准 C++ 中最为臃肿的类之一,然而却并不能满足很多开发者在日常工作中的需要。 例如, Java 和 .Net 提供了可以将字符串转换到大写字母的函数,而 std::string
就没有相应的功能。 Boost C++ 库试图弥补这一缺憾。
5.2. 区域设置
在进入正题之前,有必要先审视下区域设置的问题,本章中提到的很多函数都需要一个附加的区域设置参数。
区域设置在标准 C++ 中封装了文化习俗相关的内容,包括货币符号,日期时间格式, 分隔整数部分与分数部分的符号(基数符)以及多于三个数字时的分隔符(千位符)。
在字符串处理方面,区域设置和特定文化中对字符次序以及特殊字符的描述有关。 例如,字母表中是否含有变异元音字母以及其在字母表中的位置都由语言文化决定。
如果一个函数用于将字符串转换为大写形式,那么其实施步骤取决于具体的区域设置。 在德语中,字母 'ä' 显然要转换为 'Ä', 然而在其他语言中并不一定。
使用类 std::string
时区域设置可以忽略, 因为它的函数均不依赖于特定语言。 然而在本章中为了使用 Boost C++ 库, 区域设置的知识是必不可少的。
C++ 标准中在 locale
文件中定义了类 std::locale
。 每个 C++ 程序自动拥有一个此类的实例, 即不能直接访问的全局区域设置。 如果要访问它,需要使用默认构造函数构造类 std::locale
的对象,并使用与全局区域设置相同的属性初始化。
// // DATE: 19-4-22. // FILENAME: locale_test.cpp // AUTHOR: parzulpan // #include <locale> #include <iostream> int main() { std::locale loc; std::cout << loc.name() << std::endl; }
"/root/CLionProjects/Boost/String manipulation_test/cmake-build-debug/String_manipulation_test" C
以上程序在标准输出流输出 C
,这是基本区域设置的名称,它包括了 C 语言编写的程序中默认使用的描述。
这也是每个 C++ 应用的默认全局区域设置,它包括了美式文化中使用的描述。 例如,货币符号使用美元符号,基字符为英文句号,日期中的月份用英语书写。
全局区域设置可以使用类 std::locale
中的静态函数 global()
改变。
// // DATE: 19-4-22. // FILENAME: locale_test1.cpp // AUTHOR: parzulpan // #include <locale> #include <iostream> int main() { std::locale::global(std::locale("zh_CN.UTF-8")); std::locale loc; std::cout << loc.name() << std::endl; }
"/root/CLionProjects/Boost/String manipulation_test/cmake-build-debug/String_manipulation_test" zh_CN.UTF-8
静态函数 global()
接受一个类型为 std::locale
的对象作为其唯一的参数。 此类的另一个版本的构造函数接受类型为 const char*
的字符串,可以为一个特别的文化创建区域设置对象。 然而,除了 C 区域设置相应地命名为 "C" 之外,其他区域设置的名字并没有标准化,所以这依赖于接受区域设置名字的 C++ 标准库。
在初步理解了区域设置以及如何更改全局设置后, 下面的例子说明了区域设置如何影响字符串操作。定义在文件 cstring
中的函数 std::strcoll()
,这个函数用于按照字典顺序比较第一个字符串是否小于第二个。 换言之,它会判断这两个字符串中哪一个在字典中靠前。
5.3. 字符串算法库 Boost.StringAlgorithms
Boost C++ 字符串算法库 Boost.StringAlgorithms 提供了很多字符串操作函数。 字符串的类型可以是 std::string
, std::wstring
或任何其他模板类 std::basic_string
的实例。这些函数分类别在不同的头文件定义。 例如,大小写转换函数定义在文件 boost/algorithm/string/case_conv.hpp
中。 因为 Boost.StringAlgorithms 类中包括超过20个类别和相同数目的头文件, 为了方便起见,头文件 boost/algorithm/string.hpp
包括了所有其他的头文件。 后面所有例子都会使用这个头文件。正如上节提到的那样, Boost.StringAlgorithms 库中许多函数 都可以接受一个类型为 std::locale
的对象作为附加参数。 而此参数是可选的,如果不设置将使用默认全局区域设置。
#include <boost/algorithm/string.hpp> #include <locale> #include <iostream> #include <clocale> int main() { //实际上这里German是不行的 std::setlocale(LC_ALL, "German"); std::string s = "Boris Schäling"; std::cout << boost::algorithm::to_upper_copy(s) << std::endl; std::cout << boost::algorithm::to_upper_copy(s, std::locale("German")) << std::endl; }
函数 boost::algorithm::to_upper_copy()
用于 转换一个字符串为大写形式,自然也有提供相反功能的函数 —— boost::algorithm::to_lower_copy()
把字符串转换为小写形式。 这两个函数都返回转换过的字符串作为结果。 如果作为参数传入的字符串自身需要被转换为大(小)写形式,可以使用函数 boost::algorithm::to_upper()
或 boost::algorithm::to_lower ()
。
// // DATE: 19-4-22. // FILENAME: string_algorithms_test1.cpp // AUTHOR: parzulpan // #include <boost/algorithm/string.hpp> #include <locale> #include <iostream> int main() { std::locale::global(std::locale("zh_CN.UTF-8")); std::string s = "Boris Schaling"; std::cout << boost::algorithm::erase_first_copy(s, "i") << std::endl; std::cout << boost::algorithm::erase_nth_copy(s, "i", 0) << std::endl; std::cout << boost::algorithm::erase_last_copy(s, "i") << std::endl; std::cout << boost::algorithm::erase_nth_copy(s, "i", -1) << std::endl; std::cout << boost::algorithm::erase_all_copy(s, "i") << std::endl; std::cout << boost::algorithm::erase_head_copy(s, 6) << std::endl; std::cout << boost::algorithm::erase_tail_copy(s, 9) << std::endl; }
"/root/CLionProjects/Boost/String manipulation_test/cmake-build-debug/String_manipulation_test" Bors Schaling Bors Schaling Boris Schalng Boris Schalng Bors Schalng Schaling Boris
Boost.StringAlgorithms 库提供了几个从字符串中删除单独字母的函数, 可以明确指定在哪里删除,如何删除。例如,可以使用函数 boost::algorithm::erase_all_copy()
从整个字符串中 删除特定的某个字符。 如果只在此字符首次出现时删除,可以使用函数 boost::algorithm::erase_first_copy()
。 如果要在字符串头部或尾部删除若干字符,可以使用函数 boost::algorithm::erase_head_copy()
和 boost::algorithm::erase_tail_copy()
。
// // DATE: 19-4-22. // FILENAME: string_algorithms_test2.cpp // AUTHOR: parzulpan // #include<typeinfo> #include <boost/algorithm/string.hpp> #include <locale> #include <iostream> int main() { std::locale::global(std::locale("zh_CN.UTF-8")); std::string s = "Boris Schaling"; // boost::iterator_range<std::string::iterator> r = boost::algorithm::find_first(s, "Boris"); auto r = boost::algorithm::find_first(s, "Boris"); std::cout << typeid(r).name() << std::endl; //查看类型,返回类型为 boost::iterator_range 类的一对迭代器 std::cout << r << std::endl; r = boost::algorithm::find_first(s, "xyz"); std::cout << r << std::endl; }
"/root/CLionProjects/Boost/String manipulation_test/cmake-build-debug/String_manipulation_test" N5boost14iterator_rangeIN9__gnu_cxx17__normal_iteratorIPcNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEEEE Boris
函数 boost::algorithm::find_first()
、 boost::algorithm::find_last()
、 boost::algorithm::find_nth()
、 boost::algorithm::find_head()
以及 boost::algorithm::find_tail()
可以用于在字符串中查找子串。
所有这些函数的共同点是均返回类型为 boost::iterator_range
类 的一对迭代器。 此类起源于 Boost C++ 的 Boost.Range 库, 它在迭代器的概念上定义了“范围”。 因为操作符 <<
由 boost::iterator_range
类重载而来, 单个搜索算法的结果可以直接写入标准输出流。 以上程序将 Boris
作为第一个结果输出而第二个结果为空字符串。
// // DATE: 19-4-22. // FILENAME: string_algorithms_test3.cpp // AUTHOR: parzulpan // #include <boost/algorithm/string.hpp> #include <locale> #include <iostream> #include <vector> int main() { std::locale::global(std::locale("zh_CN.UTF-8")); std::vector<std::string> v; v.push_back("Boris"); v.push_back("Schaling"); v.push_back("hahha"); std::cout << boost::algorithm::join(v, "+") << std::endl; }
"/root/CLionProjects/Boost/String manipulation_test/cmake-build-debug/String_manipulation_test" Boris+Schaling+hahha
函数 boost::algorithm::join()
接受一个字符串的容器 作为第一个参数, 根据第二个参数将这些字符串连接起来。
// // DATE: 19-4-22. // FILENAME: string_algorithms_test4.cpp // AUTHOR: parzulpan // #include <boost/algorithm/string.hpp> #include <locale> #include <iostream> int main() { std::locale::global(std::locale("zh_CN.UTF-8")); std::string s = "Boris Bchaling"; std::cout << boost::algorithm::replace_first_copy(s, "B", "D") << std::endl; std::cout << boost::algorithm::replace_nth_copy(s, "B", 0, "D") << std::endl; std::cout << boost::algorithm::replace_last_copy(s, "B", "D") << std::endl; std::cout << boost::algorithm::replace_all_copy(s, "B", "D") << std::endl; std::cout << boost::algorithm::replace_head_copy(s, 5, "Doris") << std::endl; std::cout << boost::algorithm::replace_tail_copy(s, 8, "Becker") << std::endl; }
"/root/CLionProjects/Boost/String manipulation_test/cmake-build-debug/String_manipulation_test" Doris Bchaling Doris Bchaling Boris Dchaling Doris Dchaling Doris Bchaling Boris Becker
Boost.StringAlgorithms 库不但提供了查找子串或删除字母的函数, 而且提供了使用字符串替代子串的函数,包括 boost::algorithm::replace_first_copy()
, boost::algorithm::replace_nth_copy()
, boost::algorithm::replace_last_copy()
, boost::algorithm::replace_all_copy()
, boost::algorithm::replace_head_copy()
以及 boost::algorithm::replace_tail_copy()
等等。 它们的使用方法同查找和删除函数是差不多一样的,所不同的是还需要 一个替代字符串作为附加参数。
// // DATE: 19-4-22. // FILENAME: string_algorithms_test5.cpp // AUTHOR: parzulpan // #include <boost/algorithm/string.hpp> #include <locale> #include <iostream> int main() { std::locale::global(std::locale("zh_CN.UTF-8")); std::string s = "\t Boris Schaling \t"; std::cout << "." << boost::algorithm::trim_left_copy(s) << "." << std::endl; //自动去除字符串中最左边的空格 std::cout << "." <<boost::algorithm::trim_right_copy(s) << "." << std::endl; //自动去除字符串中最右边的空格或者字符串的结束符 std::cout << "." <<boost::algorithm::trim_copy(s) << "." << std::endl; //效果等于=trim_left_copy + trim_right_copy }
"/root/CLionProjects/Boost/String manipulation_test/cmake-build-debug/String_manipulation_test" .Boris Schaling . . Boris Schaling. .Boris Schaling.
可以使用修剪函数 boost::algorithm::trim_left_copy()
, boost::algorithm::trim_right_copy()
以及 boost::algorithm::trim_copy()
等自动去除字符串中的空格或者字符串的结束符。 什么字符是空格取决于全局区域设置。
Boost.StringAlgorithms 库的函数可以接受一个附加的谓词参数,以决定函数作用于字符串的哪些字符。 谓词版本的修剪函数相应地被命名为 boost::algorithm::trim_left_copy_if()
, boost::algorithm::trim_right_copy_if()
和 boost::algorithm::trim_copy_if()
。
// // DATE: 19-4-22. // FILENAME: string_algorithms_test6.cpp // AUTHOR: parzulpan // #include <boost/algorithm/string.hpp> #include <locale> #include <iostream> int main() { std::locale::global(std::locale("zh_CN.UTF-8")); std::string s = "--Boris Schäling--"; std::cout << "." << boost::algorithm::trim_left_copy_if(s, boost::algorithm::is_any_of("-")) << "." << std::endl; std::cout << "." << boost::algorithm::trim_right_copy_if(s, boost::algorithm::is_any_of("-")) << "." << std::endl; std::cout << "." << boost::algorithm::trim_copy_if(s, boost::algorithm::is_any_of("-")) << "." << std::endl; }
"/root/CLionProjects/Boost/String manipulation_test/cmake-build-debug/String_manipulation_test" .Boris Schäling--. .--Boris Schäling. .Boris Schäling.
以上程序调用了一个辅助函数 boost::algorithm::is_any_of()
, 它用于生成谓词以验证作为参数传入的字符是否在给定的字符串中存在。