4.复杂查找
谷歌百度的搜索功能很强,不过再牛的搜索功能也要从基础打起,基础就在母串中搜索指定的子串。
根据boost的设计,从一个母串中查找子串,返回的结果是两个值,开始位置和结束位置,
比如母串是“玛丽玛丽我爱你”,而查找的是“我爱”,则返回的(原文母串)子串分别位于8和12的迭代器,这一对迭代器构成一个“迭代范围”,体现为boost中定义的一个模板:iterator_range <T>。其中T是所要查找的字符串(也可以其他内容),
如果string,那就是string::iterator类型,如果是wstring,那就是wstring::iterator。
再次强调:返回的是两个迭代器,而不是两个数字。并且两个迭代器都指向母串身上的位置(有可能是无效位置,比如查找不到),因此如果得到具体的偏移位置,需要和母串的起始位置迭代器相减:
boost::format fmt("%s: %d ~ %d");
string s = "玛丽玛丽我爱你"; //一个汉字占用两个字节
boost::iterator_range<string::iterator> result
= str_algo::find_first(s, "我爱");
if(result)
{
cout << fmt % result
%(result.begin() - s.begin())
%(result.end() - s.begin()) << endl;
}
cout << result << endl;
从代码中可以看到,boost::iterator_range提供了转换为bool值的重载,用与判断是否找到,还提供了到字符串转换的重载,会输出其一对迭代器范围内的内容,如果找到的话,就是子串。
【危险】:iterator_range的有效期
注意,既然结果中的两个迭代器都指向母串身上的位置,所以如果在查找之后,母串内容被修改了,那么这两个迭代器就会失效,不应再访问!
还有以下几个find族函数:
//1 不区分大小写的find_first
iterator_range ifind_first(Range1T& Input
, const Range2T& Search);
//2 查找子串在母串中最后一次出现的位置(相当于倒查)
iterator_range find_last(Range1T& Input
, const Range2T& Search);
//3 查找子串在母串的第N次出现的位置(N从0开始)
iterator_range find_nth(Range1T& Input
, const Range2T& Search, int N);
//4 查找母串中的所有指定子串(不区分大小写),
//并存入Result容器中
SequenceSequenceT ifind_all(SequenceSequenceT& Result
, Range1T& Input, const Range2T& Search);
前缀 i: 表示该算法不区分大小写
后面两个函数也提供了i前缀的版本。另外string_algo也提供了基于正则表达式查找,需自行学习。
现在特别关注在母串中连续查找子串的实现。
“find_nth”可以查找母串中第N次(N从0开始)出现的子串的位置,但假设已经找到了第N个,则查找第 N+1 次出现的高效做法,应该是从上次查找的位置之后找起。下面是基于上述思路的一个"find_next"的实现:
/*自定义的find_next */
template <typename Range1T
, typename Range2T
, typename IteratorRangeT>
IteratorRangeT find_next(Range1T& Input
, Range2T const& Search
, IteratorRangeT& LastRange)
{
if(! LastRange)
{
return LastRange;
}
//搜索范围:上次找到的结束位置,到母串的结束位置
IteratorRangeT SearchRange(LastRange.end(), Input.end());
return boost::algorithm::find_first(SearchRange, Search);
}
使用例子如下:
实测结果例中的查找过程,使用 find_next 比使用 find_nth 快5倍,如果母串更长,出现子串次数更多,速度差距还会更大。
实现 find_next 的另一种做法是使用 stl 的算法,但后者的接口与 boost 此处的算法有较大区别。
最后,提供一个 wstring 的例子,如日常多数程序应使用 wstring 以便更好地支持中文:
//注意:请在 IDE 中,把代码所在源文件的编码,设置为 UTF-8
//并且本测试程序,不要和前面的string版本的个测试代码同时使用
void test_wstring_find()
{
boost::format fmt("%d ~ %d");
wstring s = L"玛丽玛丽我爱你";//注意L前缀,表示使用unicode
boost::iterator_range <wstring::iterator> result
= str_algo::find_first(s, L"我爱");//注意L前缀
if(result)
{
cout << fmt %(result.begin() - s.begin())
%(result.end() - s.begin()) << endl;
}
}
由于mingw环境的限制,不尝试在屏幕上输出宽字符,但同样输出了位置,这回是 4~6。