最近发现了一个非常好的c++开源库 C++ String Toolkit Library (StrTk) ,故整理一下。原网址如下:
简介
C ++ String Toolkit库(StrTk)是一个通用的字符串处理库。 它的设计原则以效率和易用性为主。 本教程将主要介绍该库的字符串解析string parsing和tokenization 功能。
token:令牌
tokenize:令牌化
token也可以翻译为“标记”,tokenize可以翻译为“标记解析”或“解析标记”,tokenizer可以翻译为“标记解析器”
StrTk提供了两个常见的tokenization概念,分割函数split function和令牌迭代器token iterator。 这两个概念都要求用户提供一个分隔符谓词delimiter predicate 和迭代器范围iterator range ,迭代器范围指定了tokenization将执行的范围。
tokenization分为两类种模式:
- compressed delimiters 压缩分隔符
- no compressed delimiters非压缩分隔符
使用哪种模式是可以通过参数来设置的。两种模式的本质上的区别是连续的分隔符将被压缩为一个并以此进行分割还是视为多个分隔符。以下两个表格描述了对于各种类型输入结果分割后得到的token列表。 下表分别表示“无压缩”模式和“压缩”模式下,经过tokenization 过程后得到的token列表。分隔符是管道符号| ,<>表示空标记。
| No Compressed Delimiters | Compressed Delimiters |
Input | Token List | Input | Token List |
---|---|---|---|
a | a | a | a |
a|b | a,b | a||b | a,b |
a||b | a,<>,b | |a||b | <>,a,b |
|a | <>,a | ||a||b|| | <>,a,b,<> |
a| | a,<> | | | <>,<> |
|a||b | <>,a,<>,b | || | <>,<> |
||a||b|| | <>,<>,a,<>,b,<>,<> | ||| | <>,<> |
| | <>,<> | ||
|| | <>,<>,<> | ||
||| | <>,<>,<>,<> |
通过上表对比,可以看出,在No Compressed非压缩模式下,连续的分隔符 | 将继续多次分割,而在Compressed压缩模式下,连续的分割符只会进行一次分割。
分隔符
strtk支持两种形式的分隔符,它们分别是single delimiter predicate单个分隔符谓词和multiple delimiters predicate多个分隔符谓词,分别简称为称为SDP和MDP。 基本上,SDP是只有一种类型可以分割序列,而MDP有多种可以分割序列的类型。 可以使用MDP表示SDP,但是具有单独谓词的性能POV的效率更高。 另外对于基于char或unsigned char(8位版本)的字符串,还有一个具有O(1)查找复杂度的MDP,使其比基本的MDP更有效率。
SDP
定义如下:
template <typename T>
struct single_delimiter_predicate
{
public:
typedef T value_type;
single_delimiter_predicate(const T& d)
: delimiter_(d)
{}
inline bool operator()(const T& d) const
{
return delimiter_ == d;
}
private:
single_delimiter_predicate<T>& operator=(const single_delimiter_predicate<T>&);
const T delimiter_;//常量
};
使用示例:
strtk::single_delimiter_predicate<typename T>(const T& t)
strtk::single_delimiter_predicate<std::string::value_type> predicate('|');
MDP
template <typename T>
struct multiple_delimiter_predicate
{
public:
typedef T value_type;
//构造函数一:使用指针来构造
multiple_delimiter_predicate(const T* d_begin, const T* d_end)
: length_(std::distance(d_begin,d_end)),
delimiter_((length_ <= sbo_buffer_size) ? sbo_buffer : new T[length_]),
delimiter_end_(delimiter_ + length_)
{
std::copy(d_begin,d_end, delimiter_);
}
//在长度比sbo_buffer_size小时,使用sbo_buffer
//构造函数二:使用数组来构造
multiple_delimiter_predicate(const T d[], const std::size_t& length)
: length_(length),
delimiter_((length_ <= sbo_buffer_size) ? sbo_buffer : new T[length_]),
delimiter_end_(delimiter_ + length_)
{
std::copy(d,d + length, delimiter_);
}
//构造函数二:使用迭代器来构造
template <typename Iterator>
multiple_delimiter_predicate(const Iterator begin, const Iterator end)
: length_(std::distance(begin,end)),
delimiter_((length_ <= sbo_buffer_size) ? sbo_buffer : new T[length_]),
delimiter_end_(delimiter_ + length_)
{
//static_assert(T == std::iterator_traits<Iterator>::value_type);
std::copy(begin,end, delimiter_);
}
template <typename Type>
multiple_delimiter_predicate(const range::adapter<Type>& r)
: length_(std::distance(r.begin(),r.end())),
delimiter_((length_ <= sbo_buffer_size) ? sbo_buffer : new T[length_]),
delimiter_end_(delimiter_ + length_)
{
//static_assert(T == std::iterator_traits<Iterator>::value_type);
std::copy(r.begin(),r.end(), delimiter_);
}
~multiple_delimiter_predicate()
{
if (length_ > sbo_buffer_size)
{
delete[] delimiter_;
}
}
inline bool operator()(const T& d) const
{
return (std::find(delimiter_,delimiter_end_,d) != delimiter_end_);
}
private:
multiple_delimiter_predicate(const multiple_delimiter_predicate<T>& mdp);
multiple_delimiter_predicate& operator=(const multiple_delimiter_predicate<T>& mdp);
std::size_t length_;
T* delimiter_;
T* delimiter_end_;
enum { sbo_buffer_size = 32 };
T sbo_buffer[sbo_buffer_size];
};
使用示例:
strtk::multiple_delimiter_predicate<typename T>(Iterator begin, Iterator end);
std::string str_delimiters = " ,.;:<>'[]{}()_?/'`~!@#$%^&*|-_\"=+";
strtk::multiple_delimiter_predicate mdp1(str_delimiters.begin(),str_delimiters.end());
unsigned int uint_delimiters[5] = {1,10,101,1010,10101};
strtk::multiple_delimiter_predicate<unsigned int> mdp2(uint_delimiters,uint_delimiters + 5);
Multiple Char Delimiter Predicate
这种类型的分割谓词只能够使用unsigned char或char序列进行构造。这种分隔符比的MDP更有效,因为使用查找表。
struct multiple_char_delimiter_predicate
{
public:
template <typename Iterator>
multiple_char_delimiter_predicate(const Iterator begin, const Iterator end)
{
setup_delimiter_table(begin,end);
}
multiple_char_delimiter_predicate(const std::string& s)
{
setup_delimiter_table(to_ptr(s), to_ptr(s) + s.size());
}
inline bool operator()(const unsigned char& c) const
{
return (delimiter_table_[c]);
}
inline bool operator()(const char& c) const
{
return operator()(static_cast<unsigned char>(c));
}
private:
static const std::size_t table_size = 256;
template <typename Iterator>
inline void setup_delimiter_table(const Iterator begin, const Iterator end)
{
std::fill_n(delimiter_table_,table_size,false);
for (Iterator itr = begin; itr != end; ++itr)
{
//将字符对应的ASCII值作为index,将delimiter_table_[index]设置为TRUE
delimiter_table_[static_cast<unsigned char>(*itr)] = true;
}
}
//lookup-table
bool delimiter_table_[table_size];
};
split
strtk的这个函数能够一次性完成对整个序列的切割(tokenization )。 它将提取到的token输出到输出迭代器(sink)中。 下图展示了该函数的功能:
该函数使用参数delimiters中指定的分隔符对参数data指定的数据进行分割,并将分割得到的数据即token输出到参数sink中。
以下展示了split函数的简单用法
std::string str = "abc|123|xyz|789";//待分割的字符串
strtk::std_string::token_list_type token_list;//sink
strtk::split(" |.;?",str,std::back_inserter(token_list));
strtk :: split也可以以更明确的方式使用,可以由用户指定定界符谓词的确切类型:
// split using strtk predicates
{
std::string str = "abc|123|xyz|789";
strtk::std_string::token_list_type token_list;
strtk::single_delimiter_predicate predicate('|');
strtk::split(predicate,str,std::back_inserter(token_list));
}
// split using a lambda as a predicate
{
std::string data = "abc|123|xyz|789";
std::deque<std::string> token_list;
strtk::split([](const char c)
{
return '|' == c;
},
data,
strtk::range_to_type_back_inserter(token_list));
}
strtk :: split提供了一个额外的使用选项,允许用户指定使用”compressed delimiters” 或”no compressed delimiters” 模式,以及他们是否希望将分隔符作为token的一部分。 此枚举参数称为strtk :: split_options,具有以下值:
Split Option | Definition |
---|---|
strtk::split_options::default_mode | 默认选项 |
strtk::split_options::compress_delimiters | 压缩模式,即连续的分隔符将被视为一个分隔符 |
strtk::split_options::include_1st_delimiter | 将第一个分隔符包含在分割得到的token中 |
strtk::split_options::include_delimiters | 所有的分隔符都包含在分割得到的token中 |
strtk::split(predicate,
str,
std::back_inserter(token_list),
strtk::split_options::compress_delimiters |
strtk::split_options::include_delimiters);
如下在split中使用strtk::multiple_char_delimiter_predicate:
std::string str = "abc?123;xyz.789";
strtk::std_string::token_list_type token_list;
strtk::multiple_char_delimiter_predicate predicate(" .;?");
strtk::split(predicate,str,std::back_inserter(token_list));
token_list中的结果可以使用如下方式进行输出:
strtk::std_string::token_list_type::iterator itr = token_list.begin();
while (token_list.end() != itr)
{
std::cout << (*itr) << '\t';
++itr;
}
strtk迭代函数
strtk主要用于对字符串进行tokenization ,也就是说它的处理单位是字符串,现实中我们常常需要对一个文本进行处理,显然文本是可以按行进行处理的,因此strtk提供了一系列的迭代函数,如下:
//对文件中的每一行执行function,function函数来执行tokenization
template <typename Function>
inline std::size_t for_each_line(std::istream& stream,
Function function,
const std::size_t& buffer_size = one_kilobyte)
{
std::string buffer;
buffer.reserve(buffer_size);
std::size_t line_count = 0;
while (std::getline(stream,buffer))
{
function(buffer);
++line_count;
}
return line_count;
}
template <typename Function>
inline std::size_t for_each_line_n(std::istream& stream,
const std::size_t& n,
Function function,
const std::size_t& buffer_size = one_kilobyte)
{
std::string buffer;
buffer.reserve(buffer_size);
std::size_t line_count = 0;
while (std::getline(stream,buffer))
{
function(buffer);
if (n == ++line_count)
break;
}
return line_count;
}
使用范例: