从接触C和C++,就开始对这两个语言着迷,后来接触了Java后,愈发对这两个语言着迷。不为别的,仅仅因为热爱。那种可以掌控一切的感觉,是任何一个Java程序员都体会不了的,不受制于人,自己掌握规则。
1 实现一个泛型分割函数
熟悉Java、C#以及Python的人都知道,在字符串处理时有一个split函数,非常有用,尤其在使用文件接口时,对方按照协议将内容以一定格式保存到文件中,我们要做的就是读取文件并解析文件。但是作为C/C++的我们,标准库里并没有现成的split接口可用,今天我们就使用现代C++手动实现一个C++版的分割函数。
代码实现并不复杂,先来看下代码实现:
//重新构建一个string字符串
auto binFunc([](auto itA,auto itB){
return std::string(itA,itB);
});
//字符串分割函数
template<typename T1,typename T2,typename T3,typename T4>
T1 split(T1 begIt,T1 endIt,T2 outIt,T3 diaChar,T4 Func){
while(begIt != endIt){
auto sliceEnd(std::find(begIt,endIt,diaChar));
*outIt++ = binFunc(begIt,sliceEnd);
if(sliceEnd==endIt){
return endIt;
}
begIt = std::next(sliceEnd);
}
return begIt;
}
如上,上面的接口实现了对字符串按照指定分隔符进行字符串分割。下面先来验证下代码的准确性,测试代码如下:
int main()
{
std::list<std::string> listA;
const std::string strValue{"org|userId|phoneNumber|country|addr|charge|duration|time"};
split(std::begin(strValue),std::end(strValue),back_inserter(listA),'|',binFunc);
std::copy(std::begin(listA),std::end(listA),ostream_iterator<std::string>{cout,","});
return 0;
}
测试代码构造了一个字符串,字符串使用“|”进行分割,然后调用我们上面编写好的字符串分割接口,运行结果为:
org,userId,phoneNumber,country,addr,charge,duration,time,
从结果可知,字符串被正确分割。那么上面的分割函数具体是怎么工作的呢?
2 分割函数实现分析
在上面的实现中,split函数使用std::find查找分隔符的位置并使用sliceEnd保存当前迭代器的位置,然后使用while循环保证所有遍历完所有的字符串。
binFunc函数实现了将被分割的字符串重新构建成一个string并返回。这样在split函数中,就可以被保存在outIt中。outIt在实际调用时我们传入实际上一个迭代器,通过调用std::back_inserter接口。每次都会将binFunc返回的字符串插入容器中。这样,我们就实现了将字符串进行分割并保存。下面来具体说下实现过程中使用的一些算法:
std::back_inserter在实际编程时使用不多,这里大家可以做下了解,它的定义如下:
template <class Container>
back_insert_iterator<Container> back_inserter (Container& x);
从上面的定义可知,它的参数是一个容器,主要实现的功能就是构造一个后向的迭代器并将元素插入到容器中,在上面的代码实现中就是使用了这个功能来保障分割的字符串被插入到链表中。
std::find平时使用较多,在此不做过多介绍,其定义形式如下:
template <class InputIterator, class T>
InputIterator find (InputIterator first, InputIterator last, const T& val);
即遍历[first,last]区间内的元素并返回第一个和val值相等的迭代器。
std::next主要是返回指定元素个数的迭代器,其定义如下:
template <class ForwardIterator>
ForwardIterator next (ForwardIterator it,
typename iterator_traits<ForwardIterator>::difference_type n = 1);
如上,在定义中有一个默认值参数,如果不传则默认1,在上面的字符串分割函数中就是只用默认参数。下面给出一个不使用默认参数的代码的例子。这个例子实现了容器元素的遍历并打印。
int main () {
std::list<int> mylist,outlist;
for (int i=0; i<10; i++) mylist.push_back (i*10);
std::cout << "mylist:";
std::for_each (mylist.begin(),
std::next(mylist.begin(),10),
[](int x) {
std::cout << ' ' << x;} );
std::cout << '\n';
return 0;
}
如上,代码运行后将会输出结果为:
mylist: 0 10 20 30 40 50 60 70 80 90
3 总结
其实,有很多种C++方法可以实现字符串分割功能,大家不妨参考下C++开源库boost,不得不说这个库非常强大,尤其是C++17版本中的文件处理库也来源于此。有兴趣不妨研究下。boost程序库推荐下面这本书,希望大家喜欢。
本文参考: