void find_all_words(const string& str, char decollator, vector<string> words);
其中参数decollator表示分隔符,words用于存储分离出来的各个单词(tag)。
考虑到vector没有排序的功能,调用者有可能期望把单词存储在list中,也可能根据需求的不同选择map或者其他类型的容器。所以第三个参数应该更灵活才行,可以考虑泛型但功能依然受限因为只能选择stl标准容器。
需求是多种多样的,可能用户想存放成静态数组也可能想直接打印,那么这多种多样的需求究竟能否满足呢?答案是肯定的,别忘了在c++ 0x中有两样强大的武器function和lambda表达式,所以我们第二版的函数原型如下:
void find_all_words(const string& str, char decollator, function<void(string&)> push);
它的调用看起来是下面这个样子的:
list<string> words;
find_all_words(str, ',', [&words](string& s){ words.push_back(); });
另外我们还需要对分离出来的单词做剪切操作,正常的处理逻辑是:“分离单词->剪切处理->存储单词”,当然我们可以拿到单词列表之后再挨个做剪切处理,但是这个逻辑就反了不太符合直觉而且会带来一定的效率开销(至少要再遍历一次列表),所以我们再次拿起c++ 0x的强大武器增加一个function类型的参数用于单词的预处理。函数的的调用就变成了下面的样子:
list<string> words;
auto push_word = [&words](string& s){ words.push_back(); };
auto trim_word = [](string& s){ trim(word, ' '); };
find_all_words(str, ',', push_word, trim_word);
函数原型已经确定剩下的算法问题就是小菜一碟了。函数的完整实现如下:
1 void find_all_words(const string& str, char decollator, function<void(string&)> push, function<void(string&)> pre = null)
2 {
3 string::size_type start_pos = 0;
4 do
5 {
6 string::size_type dec_pos = str.find_first_of(decollator, start_pos);
7 string word = str.substr(start_pos, dec_pos - start_pos);
8 if (pre != null) pre(word);
9 if (word.length() > 0) push(word);
10 start_pos = dec_pos != string::npos ? dec_pos + 1 : dec_pos;
11
12 } while (start_pos < str.length());
13 }
剪切string左右字符的两个函数都比较简单,实现如下:
1 void trim_left(string& s, char c)
2 {
3 if(s.length() == 0) return;
4 string::size_type pos = s.find_first_not_of(c);
5 s.erase(0, pos);
6 }
7
8 void trim_right(string& s, char c)
9 {
10 if (s.empty()) return;
11 string::size_type pos = s.find_last_not_of(c);
12 pos == string::npos ? s.clear() : s.erase(pos + 1, s.length() - pos - 1);
13 }
14
15 void trim(string& s, char c)
16 {
17 trim_right(s, c);
18 trim_left(s, c);
19 }
20
以前很少写字符串处理的程序,就是偶尔要写也习惯了用标准c函数,好处是你完全清楚每一步的操作是怎样完成的,不像string是一个神奇的黑盒子。当然更主要的原因懒得去学stl string中的一大堆方法(更郁闷的是一个方法可以有十几种重载),现在初步尝试了一下确实还是挺方便的