format
作用
格式化輸出對象,可以不改變流輸出狀态實作類似于printf()的輸出
頭檔案
#include <boost/format.hpp>
using namespace boost;
簡單的例子
//第一種用法
cout << format("%s:%d+%d=%d\n") %"sum" %1 %2 %(1+2);
//第二種用法
format fmt("(%1% + %2%) * %2% = %3%\n");
fmt %2 %5;
fmt %((2+5)*5);
cout << fmt.str();
運作結果:
sum:1+2=3
(2 + 5) * 5 = 35
說明:
- 第一種用法使用了和printf類似的文法結構,不必贅述
- 第二種用法,先執行個體化format對象确定輸出格式,使用%N%訓示參數位置
format對象操作
//傳回格式化後的字元串
formatobj.str();
//傳回已格式化的字元串長度
formatobj.size();
//清空格式和内容
formatobj.parse();
//隻清除格式
formatobj.clear();
格式化規則
- %05d:輸出寬度為5的整數,不足位用0填充
- %-8.3f:輸出左對齊,寬度總為8,小數位為3的浮點數
- % 10s:輸出10位的字元串,不足用空格填充
- 05X:輸出寬度為5的大寫16進制整數,不足填充0
- %|spec|:将格式化選項包含進兩個豎線之間,更好的區分格式化選項和普通字元
- %N%:标記弟N個參數,相當于占位符,不帶任何其他的格式化選項
format fmt1("%05d\t%-8.3f\t% 10s\t%05X\n");
cout << fmt1 %62 %2.236 %"123456789" %48;
format fmt2("%|05d|\t%|-8.3f|\t%| 10s|\t%|05X|\n");
cout << fmt2 %62 %2.236 %"123456789" %48;
執行結果:
00062 2.236 123456789 00030
00062 2.236 123456789 00030
lexical_cast
功能
對字元串進行“字面值”的轉換,對字元串與整數/浮點數之間進行轉換
需要包含的頭檔案
#inlude <boost/lexical_cast.hpp>
using namespace boost;
聲明
//标準形式,轉換數字和字元串
template <typename Target,typename Source>
inline Target lexical_cast(const Source &arg);
//轉換C字元串
template <typename Target>
inline Target lexical_cast(const char * chars,std::size_t count)
使用
在模闆參數裡指定轉換的目标類型即可
//string -> int
int x = lexical_cast<int> ("100");
// string -> float
float pai = lexical_cast<float> ("3.14159e5");
//int -> string
string str = lexical_cast<string> (456);
//float -> string
string str = lexical_cast<string> (0.618);
//hex -> string
string str = lexical_cast<string> (0x10);
【注意事項】
該模闆智能轉換字面值,如果出現不合理的轉換,例如“hello”轉int類型,則會報錯(正常人應該不會這麼幹)
錯誤處理
當lexical_cast無法執行轉換操作時會抛出異常bad_lexical_cast,它是std::bad_cast的派生類
傳統保護辦法
在使用lexical_cast時應該使用try_catch來保護代碼
try
{
cout <<lexical_cast<int>("0x100");
}
catch(bad_lexical_cast& e)
{
cout << "error: \n" << e.what() << endl;
}
//運作結果
error:
bad lexical cast:source type value could not be interpreted as target
已有庫的保護辦法
需要使用命名空間:boost::conversion
函數:
boost::conversion::try_lexical_cast(typeRaw,typeTarget);
傳回值為bool表示是否轉換成功
【技巧:驗證數字字元串的合法性(用于驗證使用者輸入的有效性)】
實作一個模闆類
template<typename T>
bool num_valid(const char* str)
{
T tmp;
return conversion::try_lexical_convert(str,tmp) //嘗試轉換數字
}
//用法
assert(num_valid<double>("3.14"));
assert(!num_valid<int>("3.14"));
assert(num_valid<int>("65535"));
轉換要求
lexical_cast對轉換對象有一定要求
-
轉換的起點對象是可流輸出的(可以用“<<”)
【注意事項】對于重載了“<<”操作符的自定義類型也可以使用它
- 轉換的終點對象是可流輸入的(可以用“>>”)
- 轉換的終點對象是可預設構造的、可拷貝構造的
最常用的搭檔:int,double,string等POD類型
C++标準轉換函數
//字元串轉換為數字
int stoi(const string& str,size_t *idx = 0,int base = 10);
long stol(const string& str,size_t *idx = 0,int base = 10);
long long stoll(const string& str,size_t *idx = 0,int base = 10);
float stof(const string& str,size_t *idx = 0);
double stod(const string& str,size_t *idx = 0);
//數字轉換為string
string to_string(Type val);
【注意事項】必須以空格或數字開頭,否則報錯
和lexical_cast的比較:
優點:
- 無需寫模闆參數
- 允許出現非數字字元(忽略起始空格,遇到無法轉換的字元終止)
缺點:
- 不支援對自定義類型的轉換
string_algo
功能
提供了強大的字元串處理能力,如查找、通路、基本的字元串處理
頭檔案和命名空間
#include <boost/algorithm/string.hpp>
using namespace boost;
用法
【注意事項】不僅可以用在
string
上(在這裡
string
被看作是
vector<char>
),也可以用于部分其他容器,例如(
vector<T>
)
大小寫轉換
string str("Hello");
//轉向大寫
cout << to_upper(str) << endl; //這種方式會改變源資料
cout << to_upper_copy(str) << endl; //這種方法傳回一個轉換後的拷貝對象
//轉向小寫
cout << to_lower(str) <<endl;
cout << to_lower_copy(str) << endl;
判斷式(算法)
-
:根據字典順序檢測一個字元串是否小于另一個字元串lexicographical_compare
-
:檢測字元串是否以另一個字元串為字首starts_with
-
:檢測字元串是否以另一個字元串為字尾ends_with
-
:檢測字元串是否包含另一個字元串contains
-
:檢測兩個字元串是否相等equals
-
:檢測字元串是否滿足指定的判斷式all
【注意事項】
- 除了all以外都有一個i字首的版本,表示大小寫無關
- 這些函數都不變動字元串
用法示例
string str("Power Bomb");
assert(iends_with(str,"bomb")); //大小寫無關檢測字尾
assert(!ends_with(str,"bomb")); //大小寫敏感檢測字尾
assert(starts_with(str,"Pow")); //檢測字首
assert(contains(str,"er")); //測試包含關系
string str2 = to_lower_copy(str); //轉換成小寫
assert(iequals(str,str2)); //大小寫無關判斷相等
assert(ilexicographical_compare(str,str3)); //大小寫無關字元串比較
assert(all(str2.substr(0,5),is_lower())); //檢測字元串均小寫
分類
提供一組分類函數,用于檢測字元串是否符合某種特性,主要搭配其他算法使用,如上一節的all
-
:字元是否為空格或制表符(tab)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
-
:字元是否位于指定區間内,即from<=ch<=toif_from_range
需要注意的是這些函數并不真正地檢測字元,而是傳回一個類型為detail::is_classifiedF的函數對象,這個函數對象的operator()才是真正的分類函數(是以,這些函數都屬于工廠函數)。
修剪
提供三個算法,删去字元串開頭結尾的空格,提供_copy字尾和_if字尾
-
:删除左邊的空格trim_left
-
:删除右邊的空格trim_right
-
:删除兩邊的空格trim
用法示例
format fmt("|%s|\n");
string str = " samus aran ";
cout << fmt % trim_copy(str); //删除兩端的空格
cout << fmt % trim_left_copy(str); //删除左邊的空格
trim_right(str); //原地删除
cout << fmt % str;
string str2 = "2020 Happy new Year!!!";
cout << fmt % trim_left_copy_if(str2,is_digit()); //删除左端的數字
cout << fmt % trim_right_copy_if(str2,is_punct()); //删除右邊的标點
//删除兩端的标點、數字和空格
cout << fmt % trim_copy_if(str2,is_punct() || is_digit() || is_space());
執行結果
|samus aran|
|samus aran |
| samus aran|
| Happy new Year!!!|
|2020 Happy new Year|
|Happy new Year|
查找
提供的查找算法如下:
-
:查找字元串在輸入中第一次出現的位置find_first
-
:查找字元串在輸入中最後一次出現的位置find_last
-
:查找字元串在輸入中的第N次(從0開始計數)出現的位置find_nth
-
:取一個字元串開頭N個字元的子串,相當于substr(0,n)find_head
-
:取一個字元串末尾N個字元的子串find_tail
【注意事項】
這些算法的傳回值是
iterator_range
,在概念上類似于
std::pair
,包裝了兩個疊代器,可以用
begin()
和
end()
通路。提供了i字首的用法。
用法示例
format fmt("|%s|.pos = %d\n");
string str = "Long long ago , there was a king.";
iterator_range<string::iterator> rge; //疊代器區間
rge = find_first(str,"long"); //找第一次出現
cout << fmt % rge % (rge.begin() - str.begin());
rge = ifind_first(str,"long"); //大小寫無關第一次出現
cout << fmt % rge % (rge.begin() - str.begin());
rge = find_nth(str,"ng",2); //找第三次出現
cout << fmt % rge % (rge.begin() - str.begin());
rge = find_head(str,4); //取前4個字元
cout << fmt % rge % (rge.begin() - str.begin());
rge = find_tail(str,5); //取末5個字元
cout << fmt % rge % (rge.begin() - str.begin());
rge = find_first(str,"samus"); //找不到
assert(rge.empty() && !rge);
執行結果
|long|.pos = 5
|Long|.pos = 0
|ng|.pos = 30
|Long|.pos = 0
|king.|.pos = 28
替換與删除
替換、删除操作與查找算法非常接近,是在查找到結果後再對字元串進行處理,具體如下:
replace
/
erase_first
:替換/删除字元串在輸入中的第一次出現
replace
/
erase_last
:替換/删除字元串在輸入中的最後一次出現
replace
/
erase_nth
:替換/删除字元串在輸入中的第n次出現
replace
/
erase_all
:替換/删除字元串在輸入中的所有出現
replace
/
erase_head
:替換/删除輸入的開頭
replace
/
erase_tail
:替換/删除輸入的末尾
前8個算法每個都有字首i、字尾_copy的組合,有四個版本,後4個則隻有字尾_copy的兩個版本
示例代碼如下:
// 替換和删除
string str_2 = "Samus beat the monster.\n";
// replace
cout << replace_first_copy(str_2, "Samus", "samus");
replace_last(str_2, "beat", "kill");
cout << str_2;
replace_tail(str_2, 9, "ridley.\n");
cout << str_2;
// delete
cout << ierase_all_copy(str_2, "samus ");
cout << replace_nth_copy(str_2, "l", 1, "L");
cout << erase_tail_copy(str_2, 8);
運作結果:
samus beat the monster.
Samus kill the monster.
Samus kill the ridley.
kill the ridley.
Samus kilL the ridley.
Samus kill the
分割
string_algo提供了兩個字元串分割算法:
find_all
(雖然它的名稱含有find,但因為其功能而被歸類為分割算法)和split,可以使用某種政策把字元串分割成若***分,并将分割後的字元串拷貝存入指定的容器。
分割算法對容器類型的要求是必須能夠持有查找到結果的拷貝或引用,是以容器的元素類型必須是
string
或
iterator_range<string::iterator>
,容器則可以是vector、
list
、deque等标準容器。
find_all
算法類似于普通的查找算法,它搜尋所有比對的字元串,将其加入容器,有一個忽略大小寫的字首i版本。
split
算法使用判斷式Pred來确定分割的依據,如果字元ch滿足判斷式
Pred(Pred(ch)==true)
,那麼它就是一個分割符,将字元串從這裡分割。
還有第三個參數
eCompress
可以取值為
token_compress_on
或
token_compress_off
,如果值為前者,那麼當兩個分隔符連續出現時,它們将被視為一個分隔符,如果值為後者則兩個連續的分隔符标記了一個空字元串。參數
eCompress
的預設取值為
token_compress_off
。
string str = "Samus, Link.Zelda::Mario-Luigi+zelda";
deque<string> d;
// 大小寫無關查找
ifind_all(d, str, "zELDA");
assert(d.size() == 2);
for(auto x: d)
{
cout << "[" << x << "]";
}
cout << endl;
// 存儲range對象
list<iterator_range<string::iterator>> l;
split(l, str, is_any_of(",.:-+")); // 使用标點分割
for(auto x: l)
{
cout << "[" << x << "]";
}
cout << endl;
l.clear();
split(l, str, is_any_of(",.:-+"), token_compress_on);
for(auto x: l)
{
cout << "[" << x << "]";
}
cout << endl;
程式運作結果如下:
[Zelda][zelda]
[Samus][ Link][Zelda][][Mario][Luigi][zelda]
[Samus][ Link][Zelda][Mario][Luigi][zelda]
合并
合并算法join是分割算法的逆運算,它把存儲在容器中的字元串連接配接成一個新的字元串,并且可以指定連接配接的分隔符。
join還有一個字尾_if的版本,它接收一個判斷式,隻有滿足判斷式的字元串才能合并。
vector<string> v = list_of("Samus")("Link")("Zelda")("Mario");
cout << join(v, "+") << endl;
cout << join_if(
v, "**",
[](string_ref s)
{
return contains(s, "a");
}
) << endl;
程式首先使用assign庫向
vector
添加了4個字元串,然後用
+
合并它們。随後的
join_if
算法使用lambda表達式定義了一個簡單的謂詞,它包裝了算法
contains
,判斷字元串是否包含字元a。
程式運作結果如下:
Samus+Link+Zelda+Mario
Samus**Zelda**Mario
string_ref
功能
一種輕量級的string,持有string類型的引用
頭檔案
#include <boost/utility/string_ref.hpp>
using namespace boost;
類摘要
template<typename charT,typename traits>
class basic_string_ref
{
public:
//和std::string有着幾乎一樣的接口
private:
const charT* ptr_; //字元串指針
std::size_t len_; //字元串長度
};
不拷貝字元串,是以不配置設定記憶體,使用兩個成員變量表示字元串
用法
【注意事項】隻能像std::string&一樣去擷取其内容,但不能修改其本身
1、構造
//通過标準字元數組構造普通string,有拷貝成本
const char* ch = "hello";
string str(ch);
//字元數組構造,無成本
string_ref s1(ch);
//标準字元串構造,無成本
string_ref s2(str);
可以像使用普通string一樣使用string_ref(除了修改)
2、用在哪
用于代替string&作為函數參數和傳回值,可以完全避免字元串拷貝代價