Posted on March 12th, 2007 at 21:41 by fr3@K
一直以來, 對 C++ Standard Library 裡面沒有字串大小寫轉換的 function, 除了納悶還是納悶. 在 <algorithm> 裡面沒有, 即便是提供了典型 fat interface 的 std::basic_string<> , 也獨漏 upper/lower.
舉兩個例子; 既然有了標準容器都支援的 std::basic_string<>::size 又何必要有 std::basic_string<>::length? 與搜尋相關的 member function 如 std::basic_string<>::find/find_first_of 等, 全部都可以在 <algorithm> 找到可更被泛用的 function template.
好吧, Standard Library 裡面隨手可用的 algorithm/facility 這麼多, 這一定是為了簡化 interface 而刻意省略的 (讓我們先忘記它其實是個 fat interface 的事實), 要不了幾行 code 一定就能寫出一個 STLified 的 function (template). 有人可能會這樣說. 那我們就來試試看.
首先要定義一個 STLified 的 interface, 要能使用 iterator 語法, 以及提供 char/wchat_t transparency1. 這裡提出的是個類似 std::copy 的 interface:
template <class InputIterator, class OutputIterator>
void copy_lower(
InputIterator first,
InputIterator last,
OutputIterator dest);
這個 interface 可以被這樣使用:
template <class InputIterator>
void foo(
const std::string& str,
const std::wstring& wstr,
InputIterator first, InputIterator last)
{
std::string bar(str);
copy_lower(bar.begin(), bar.end(), bar.begin());
std::wstring faux;
faux.reserve(wstr.size()); // optionally, preallocates memory
copy_lower(wstr.begin(), wstr.end(), std::back_inserter(faux));
typedef typename
std::iterator_traits<InputIterator>::value_type
char_type;
typedef
std::basic_string<char_type, char_traits<char_type> >
xstring;
xstring foobar;
copy_lower(first, last, std::back_inserter(foobar));
}
接著我們來試著實做. 嘗試 1, copy_lower_c, 使用 C 的 global locale:
#include <string>
#include <algorithm>
#include <cctype>
template <class InputIterator, class OutputIterator>
void copy_lower_c(
InputIterator first,
InputIterator last,
OutputIterator dest);
{
std::transform(first, last, dest, tolower);
}
copy_lower_c 的實做非常簡單, 也幾乎符合之前提出的需求, 只可惜 tolower 是給 char 用的 (相對於 wchar_t)2.
C++ 對 wchar_t 的支持顯然比較好, 用 C++ 的 locale 應該能搞定. 研究 reference 後的確看到不只一個能拿來當零件用的 algorithm/facility. 嘗試 2, copy_lower_cpp, 使用 C++ 的 global locale:
#include <string>
#include <iterator>
#include <algorithm>
#include <locale>
template <class CharType>
class lowerer
{
public:
explicit lowerer (const std::locale& loc)
: loc_(loc)
{}
CharType operator() (CharType ch)
{
return std::tolower(ch, loc_);
}
std::locale loc_;
};
template <class InputIterator, class OutputIterator>
void copy_lower_cpp(
InputIterator first, InputIterator last,
OutputIterator dest)
{
typedef typename
std::iterator_traits<InputIterator>::value_type
char_type;
std::transform(
first, last, dest,
lowerer<char_type>(std::locale()));
}
本來期望可以用 std::bind2nd<> 把一個 std::locale 的 instance 綁在 std::tolower 的第二個參數, 但 std::bind2nd<> 只吃符合 Adaptable Binary Function 的 functional object, 也就是說一樣要我們自己寫一個給 std::tolower<> 這個 function template 的 adapter class. Code 的複雜度與美觀程度與 copy_lower_cpp 版本不相上下.
嘗試 3, copy_lower_boost, 用 Boost.Lambda 來避免人工製作這個 functional object adapter class:
#include <string>
#include <iterator>
#include <algorithm>
#include <locale>
#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>
template <class InputIterator, class OutputIterator>
void copy_lower_boost(
InputIterator first,
InputIterator last,
OutputIterator dest)
{
typedef typename
std::iterator_traits<InputIterator>::value_type
char_type;
std::transform(
first, last, dest,
boost::lambda::bind(
std::tolower<char_type>,
boost::lambda::_1,
std::locale()));
}
應用了 Boost, copy_lower_boost 不但符合之前定下的需求, 其 function body 也只是簡潔一行 code (不算 typedef). 即使如此這樣的一行 code (function body) 依然比不上呼叫一個簡潔的標準函式來得容易以及 error free. 我還是期待如這般功能更強大的字串處理, 在可以避免造成 fat interface 的情況下, 被定義進未來版本的 Standard Library.
以上 source code 可以在 這裡 下載 (LGPL licensed). 解壓縮後在命令列下 `./configure CXXFLAGS="-Iinclude -Wall" && make' 就可以編譯.
![]() |
|
| Previous Post « Firefox Update Breaks Evolution « |
Next Post » Web Application Development Screencasts » |








tolower 應該是適合在拉丁語系的國家
wchar_t 定義這種 tolower function 反而變得很怪
老實說 我覺得只是要讓東西能動
STL 不是很棒的選擇
不過就美學的觀點來說是很棒的 XD
Comment by spequer — March 13, 2007 @ 2:13