一起學習Boost標準庫--Boost.StringAlgorithms庫
概述
在未使用Boost庫時,使用STL的std::string處理一些字符串時,總是不順手,特別是當用了C#/Python等語言後trim/split總要封裝一個方法來處理。如果沒有形成自己的common代碼庫,那就悲劇了,每用一次都要寫一次,雖然難度不大,但是每次重復這樣工作也還是比較費勁。一般通過STL進行封裝如下:
// trim from start
inline std::string &LeftTrim(string &s) {
s.erase(s.begin(), std::find_if(s.begin(), s.end(),
std::not1(std::pointer_to_unary_function<int , int>(isspace))));
return s;
}
// trim from end
inline std::string &RightTrim(string &s) {
s.erase(std::find_if(s.rbegin(), s.rend(),
std::not1(std::pointer_to_unary_function<int, int>(isspace))).base(), s.end());
return s;
}
// trim from both ends
inline tstring &Trim(tstring &s)
{
return LeftTrim(RightTrim(s));
}
inline std::vector<string> &Split(const string &s, char delim,
std::vector<string> &elems, bool bKeepEmpty=false)
{
stringstream ss(s);
string item;
while(std::getline(ss, item, delim)) {
if(item.empty() && !bKeepEmpty)
continue ;
elems.push_back(item);
}
return elems;
}
inline std::vector<string> Split(const string &s, char delim) {
std::vector<tstring> elems;
return Split(s, delim, elems);
}
同時我還是那種沒有common代的一類懶漢。當開始使用Boost的時候,特別是看到Boost.StringAlgorithms庫時,我可以說,我內心是激動的嗎?居然有一個這麽順手的工具,過去我居然沒有用,為什麽STL這個標準庫不加上它呢?廢話不多說,謹記習大大箴言:擼起袖子,就是幹!
STL標準庫中std::string有一些成員函數可以查找子串/訪問字符和執行基本的字符串功能。std::string可以把它看成是char類型的序列容器(標準庫定義如下代碼),可以使用標準庫中的算法進行處理,但是畢竟這些算法不是專門未字符串而寫,故有時候就稍顯‘笨拙‘
typedef std::basic_string<char> std::string;
typedef std::basic_string<wchar_t> std::wstring;
Boost.Boost.StringAlgorithms庫的出現改變了這個局面,其內包含了非常全面的字符串算法庫,提供了大量的方法來操作字符串,比如:分割/修剪/特定模式字符串查找等等一些列方法。
雖然Boost.StringAlgorithms是被設計用來處理字符串的,但是它還有更強悍的功能,並不一定是string,wstring或者任何以basic_string的模版類,可以是符合boost.range(後面回專門講解)要求的容器,他可以是容器對象如:vector/deque/list等,不過本文主要還是定位到字符串的處理。
首先Boost.StringAlgorithms庫需要包含頭文件"boost/algorithm/string.hpp"中,位於命名空間boost::algorithm,但是被using語句引入到了boost命名空間。可通過如下代碼進行引入:
#include <boost/algorithm/string.hpp>
using namesapce boost;
註: 該庫遵循標準庫的命名規範,使用小寫形式,同時通過不同前後綴來區分不同版本,具體規則如下
- 前綴i:忽略大小寫
- 後綴_copy: 不改變當前字符串,返回處理後的拷貝結果,否則就是在原字符串上進行調整操作
- 後綴_if: 需要一個作為判斷式的謂詞函數對象
本文從如下幾個方面進行介紹:
- 大小寫轉化
- 判斷式
- 分類
- 修剪
- 查找及叠代器
- 替換和刪除
- 分割與合並
大小寫轉化
Boost.StringAlgorithms庫可以快速高效的將字符串進行大小寫轉換,主要使用如下兩個方法,帶後綴_copy的方法把輸入轉換後進行輸出
to_upper(); //字符串轉化成大寫形式
to_lower(); //字符串轉換成小寫形式
測試代碼:
void test_to_case()
{
string str = "The C++ Boost Libraries";
cout << str << endl;
cout << to_upper_copy(str) << endl;
cout << str << endl;
to_lower(str);
cout << str << endl;
}
輸出結果如下:
The C++ Boost Libraries
THE C++ BOOST LIBRARIES
The C++ Boost Libraries
the c++ boost libraries
判斷式
判斷式算法可以檢測兩個字符串之間的關系,包括一下幾種,此處我將函數對象也羅列到一起:
lexicongraphical_compare(str1, str2) //按照字典順序判斷字符串大小
starts_with(str1, str2) //判斷字符串是否以另一個開始
ends_with(str1, str2) //判斷字符串是否以另一個結束
contains(str1, str2) //判斷字符串是否包含另一個
equals(str1, str2) //兩個字符串是否相等
all(str, pred) //檢測字符串中所有元素是否滿足指定謂詞
is_equal()(str1,str2) //函數對象,判斷字符串是否相等
is_less()(str1, str2) //函數對象,判斷字符串str1是否小於str2
is_not_greater()(str1,str2) //函數對象,判斷字符串str1是否不大於str2
註: 函數對象後面有兩對括號,第一對括號是創建一個臨時對象;第二對括號是調用操作符operator()
測試代碼如下:
void test_compare_string()
{
string str = "Boost C++ Libraries";
cout.setf(std::ios::boolalpha);
cout << starts_with(str, "Boost") << endl; // true
cout << ends_with(str, "Libraries") << endl; // true
cout << contains(str, "C++") << endl; // true
cout << equals(str, to_lower_copy(str)) << endl; // false
cout << all(str, is_lower()) << endl; // false
string str2 = "STL C++ Libraries";
cout << is_equal()(str, str2) << endl; // false
string str3 = to_upper_copy(str);
cout << is_less()(str, str3) << endl; // false
}
分類
Boost.StringAlgorithms提供一組分類函數(謂詞對象),檢測一個字符是否符合某種特性。
is_space() // 字符是否為空格或制表符
is_alnum() // 字符是否為字母或數字
is_alpha() // 字符是否為字母
is_cntrl() // 字符是否為控制字符
is_digit() // 字符是否為十進制數字
is_graph() // 字符是否為圖形字符
is_lower() // 字符是否為小寫字符
is_print() // 字符是否為可打印字符
is_punct() // 字符是否為表點字符
is_upper() // 字符是否為大寫字符
is_xdigit() // 字符是否為十六進制字符
is_any_of() // 字符是否為字符序列中任意字符
if_from_range() // 字符是否位於指定區間中
這些函數並不真正檢測字符,而是返回一個函數對象,通過調用函數對象的operator()來進行分類判斷,內部實現如下:
inline detail::is_classifiedF
is_alpha(const std::locale& Loc=std::locale())
{
return detail::is_classifiedF(std::ctype_base::alpha, Loc);
}
示例代碼:
void test_classified()
{
std::string s = "Boost C++ Libraries";
std::vector<std::string> vs;
split(vs, s, is_space());
std::cout << vs.size() << endl; // 3
}
修剪
Boost.StringAlgorithms提供三個修剪函數,如前文我們一般實現的代碼,分別修剪字符串開頭或結尾的空格(該方法的_if版本可接收其他謂詞對象,如上文提到的分類)
trim_left()
trim_right()
trim()
示例代碼如下:
void test_trim()
{
std::string s = " Boost C++ Libraries!!!--166";
cout << trim_left_copy(s) << endl; //默認去除空格
cout << trim_right_copy_if(s, is_digit() || is_space()) << endl;//通過||組合去除數字和空格
cout << trim_copy_if(s, is_space() || is_punct() || is_digit()) << endl;
cout << trim_copy_if(s, is_any_of("! -61B")) << endl;
}
此處調用is_any_of謂詞方法,生成謂詞以驗證作為參數傳入的字符是否在給定的字符串中存在
結果如下:
Boost C++ Libraries!!!--166
Boost C++ Libraries!!!--
Boost C++ Libraries
oost C++ Libraries
查找
以下方法返回一個叠代器的pair對象boost::iterator_range,其內部定義為:std::pair
find_first() //查找字符串再輸入中第一次出現的位置
find_last() //查找字符串再輸入中最後一次出現的位置
find_nth() //查找字符串再輸入中第N次出現的位置(其中N的索引從0開始)
find_head() //取一個字符串的N個開頭,和string的substr(0,N)取字串類似
find_tail() //取一個字符串的末尾N個字符子串
find_iterator()
//內部定義如下
template<typename Range1T, typename Range2T>
inline iterator_range find_first(Range1T& Input, const Range2T& Search)
template<typename Range1T, typename Range2T>
inline iterator_range find_nth(Range1T& Input, const Range2T& Search, int Nth)
template<typename Range1T, typename Range2T>
inline iterator_range find_head(RangeT& Input, int N)
測試代碼如下:
void test_find()
{
std::string s = "Boost C++ Libraries";
iterator_range<std::string::iterator> iter = ifind_first(s, "c++");
cout << iter << endl; //C++
cout << "begin: " << *iter.begin() << ", end: " << *--iter.end() << endl; //begin: C, end: +
iter = find_head(s, 5);
cout << iter << endl; //Boost
}
替換和刪除
Boost.StringAlgorithms提供方法對字符串進行替換或刪除操作與查找字符串方法很類似,是再查找後再對其進行替換刪除操作,
replace/erase_first() // 替換/刪除一個字符串再輸入中第一次出現
replace/erase_last() // 替換/刪除一個字符串再輸入中最後一次出現
replace/erase_nth() // 替換/刪除一個字符串再輸入中第N次出現
replace/erase_all() // 替換/刪除一個字符串再輸入中所有出現
replace/erase_head() // 替換/刪除輸入的開頭
replace/erase_tail() // 替換/刪除輸入結尾
測試代碼如下:
void test_replace()
{
std::string s = "Boost C++ Libraries";
cout << replace_first_copy(s, "+", "-") << endl;
cout << replace_nth_copy(s, "+", 1, "-") << endl;
cout << replace_head_copy(s, 5, "STL") << endl;
replace_last(s, "s", "s ");
cout << replace_tail_copy(s, 1, " is very good") << endl;
cout << erase_first_copy(s, "C++") << endl;
cout << erase_all_copy(s, " ") << endl;
}
結果如下:
Boost C-+ Libraries
Boost C+- Libraries
STL C++ Libraries
Boost C++ Libraries is very good
Boost Libraries
BoostC++Libraries
分割與合並
Boost.StringAlgorithms提供了兩個分割字符串的方法,find_all和split使用某種策略把字符串分隔成若幹部分,並將分割後的字符串存入指定的容器。其中split需要給定一個謂詞作為第三個參數以判斷應該在字符串的哪個位置分割,find_all類似於普通的查找方法,它搜索所有匹配的字符串,加入到容器。
join是分隔算法的逆運算,它吧存儲再容器中的字符串,通過指定的分隔符練成一個新的字符串
find_iterator和split_iterator可以再字符串中像叠代器那樣遍歷容器,執行操作或分隔
find_all()
split()
find_iterator()
split_iterator()
join()
示例代碼如下:
void test_split_and_join()
{
std::string s = "Boost C++ Libraries BOOST c++ libraries LIBRARIES LIBraries";
vector<std::string> vs;
ifind_all(vs, s, "boost");
cout << vs.size() << endl; //2
list<iterator_range<string::iterator>> items;
split(items, s, is_space());
cout <<"size: "<< items.size() //size: 9
<< " the first: " << *items.begin() << endl; // the first: Boost
list<std::string> ls = assign::list_of("Boost")("C++")("Libraries");
cout << join(ls,"*") << endl; //Boost*C++*Libraries
}
一起學習Boost標準庫--Boost.StringAlgorithms庫