boost的字符串處理函數——string algorithm


c++在stl庫中提供了一個string類用以代替c語言的char*來實現字符串功能,不過stl的string只提供了一個連接字符串和查找的功能,其它的常用函數幾乎一律沒有,就連字符串替換都得自己來實現,和c#的字符串函數比起來簡直弱爆了。

boost庫在頭文件<boost/algorithm/string.hpp>中提供了不少字符串處理函數,用以幫助我們實現基本的字符串處理功能,極大程度上緩解了字符串函數不夠用的問題。

    string str1("hello abc-*-ABC-*-aBc goodbye");
    vector<string> SplitVec; // #2: Search for tokens
    split(SplitVec, str1, is_any_of("-*"), token_compress_on); // SplitVec == { "hello abc","ABC","aBc goodbye" }

上述代碼就提供了一個split的功能,不過和c#的版本相比,還是不夠簡潔,如果設計成這樣就更加好用了:

    auto SplitVec = split(str1, is_any_of("-*"), token_compress_on);

之所以不以這種形式設計,貌似是因為如果在split函數里構造返回值的話,會有一次數據拷貝的開銷。從中也可以看出,boost的設計還是以性能為主的。而.net程序天然沒有返回值拷貝帶來的開銷,可以采取那種更簡潔的接口形式。

PS:我覺得可以像.net那樣,在堆變量中保存結果,然后通過auto_ptr封裝后作作為返回值。即有簡潔的接口形式,又沒有數據拷貝的開銷,堆變量也能通過auto_ptr自動釋放。

同樣,也是出於性能方向的考慮,對於會產生新字符串的函數,往往會有一個xxx和xxx_copy兩個版本,例如tolower就有to_lower和to_lower_copy兩個版本:一個是直接變更本身的值,一個是構造新字符串,具體選取那個根據實際場景來考慮。

另外,字符串比較函數中,往往有區分大小寫和不區分大小寫的算法。在boost中並不是通過參數來控制,而是直接提供xxx和ixxx兩個版本,其中i就表示ignor case。如equals和iequals。還有的需要和條件函數一並使用的,這時就會提供一個xxx_if的版本。

由於提供的字符串函數比較多,這里我只按每類列舉幾個常用的函數和示例,如果同一個算法有xxx_copy的版本或ixxx版本,也只列舉基本的形式。其它的情參看boost文檔

Case Conversion

大小寫轉換涉及到四個函數:to_upper()to_upper()以及xxx_copy的版本。基本用法如下:

    cout << to_upper_copy(string("hello world")) << endl;

Trimming

Trimming函數主要有trim()trim_left()trim_right()和他們的xxx_copy和xxx_if版本。用於去除字符串首位的空白字符:

    cout << trim_copy(std::string(" hello world")) << endl;

當然,也不限於只去掉空白字符:

    cout << trim_copy_if(std::string(",,,hello world"), is_any_of(" ,.:")) << endl;

Predicates

Predicates函數主要有:starts_with() 、ends_with() 、contains() 、equals() 、lexicographical_compare() 、all()以及他們的ixxx版本。基本上從名字里就可以看出怎么用了:

    cout << (ends_with("hello world", "world") ? "true" : "false") << endl;

Find algorithms

查找算法有好幾種:find()、first()find_last()find_nth() 、find_head()find_tail()find_token()find_regex()。常見的用法如下:

    auto result = find_first("hello world", "world");
    if(result.empty())
        cout << "can't find result" << endl;
    else
        cout << result << endl;

其中result是一個boost::iterator_range類型的對象,可以用它來構造子串。

    string s("hello world");
    cout << s.substr(find_first(s, "wo").begin() - s.begin()) <<endl;

或者來個更復雜點的:

    string str1("abc-*-ABC-*-aBc");
    for(auto it = make_find_iterator(str1, first_finder("abc", is_iequal())); !it.eof(); ++it)
    {
        cout << copy_range<std::string>(*it) << endl;
    }

Erase/Replace

boost把erase和replace函數分開來列了,這樣的好處是命名比較清晰,但不好的地方時函數變得非常多,不如重載的形式那么好記。

replace的函數有replace_all() 、replace_first()、 replace_last() 以及它們的變體,加上erase,共有20多種,這里就不一一列舉了。

    cout << replace_all_copy(string("hello world"), "l", "-") << endl;

Split

split函數的用法前面以及介紹過:

    string str1("hello abc-*-ABC-*-aBc goodbye");
    vector<string> SplitVec;
    split(SplitVec, str1, is_any_of("-*"),
token_compress_on);

需要注意的是這里的token_compress_on參數,它可以吧連續多個分隔符當一個,默認沒有打開,當用的時候一般是要打開的。

另外,boost把find_all函數也分到split一類里面去了,它們的用法到也確實類似。

    string str1("hello abc-*-ABC-*-aBc goodbye");
    typedef vector<iterator_range<string::iterator>> find_vector_type;
    find_vector_type FindVec;
    ifind_all( FindVec, str1, "abc" );

Join

Join函數則是和split相反,用於把多個字符串拼接起來。

    std::array<string, 3> k = {"hello", "world", "123"};
    cout << join(k, "-");        //
輸出結果為: hello-world-123

它要求先把參數放到容器里,不像.net的那樣可以直接傳入動態參數那樣好用,如果能寫成這樣的重載形式會更好點(當然,自己封一個也不難):

    join("-", "hello", "world", "123", …)

其它

其它還有一些條件函數,主要配合算法使用。如前面已經見過的is_any_of()、常用的還有is_upper()is_lower()is_digit()is_space()等,這里就不多介紹了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM