编辑:我添加了一个最小的解决方案。
最小的解决方案
假设我正确理解了这个问题,我会做的就是定义一个合适的分割函数。
由于在 split 的定义中似乎忽略了拆分空字符串,我希望以下拆分函数可以解决当前问题。
该函数统计非空字符串的个数,最后创建最后一个非空字符串:
std::pair<std::size_t, std::string>
check(const std::string& str, const std::string& delimiter)
{
std::size_t count = 0;
auto begin = str.begin();
auto end = str.begin();
for(auto it = str.begin();
it != str.cend();
it += ((it == str.cend()) ? 0 : delimiter.length()) )
{
const auto preit = it;
it = std::search(preit, str.cend(), delimiter.cbegin(), delimiter.cend());
if(it != preit)
{
++count;
begin = preit;
end = it;
}
}
return { count, std::string(begin, end) };
}
如下使用这个分割函数,我们可以得到想要的输出3 abcd。
DEMO is here.
std::string s = ">=scott>=tiger>=mushroom>=>=abcd>=";
std::string delimiter = ">=";
const auto result = check(s, delimiter);
if(result.first != 0){
std::cout << (result.first - 1) << " " << result.second << std::endl;
}
一般拆分函数
如果还需要其他非空拆分字符串,最好实现一个更通用的拆分函数。
此外,由于通用拆分函数在其他问题中很有用,因此我认为这是一个很好的机会。
在上述函数check的if部分创建字符串并将其放回std::vector,我们得到一个更通用的拆分函数:
std::vector<std::string>
split(const std::string& str, const std::string& delimiter)
{
std::vector<std::string> strings;
for(auto it = str.begin();
it != str.cend();
it += ((it == str.cend()) ? 0 : delimiter.length()) )
{
const auto preit = it;
it = std::search(preit, str.cend(), delimiter.cbegin(), delimiter.cend());
if(it != preit){
strings.emplace_back(preit, it);
}
}
return strings;
}
如下使用这个拆分函数,我们再次得到想要的输出3 abcd。
DEMO is here.
std::string s = ">=scott>=tiger>=mushroom>=>=abcd>=";
std::string delimiter = ">=";
const auto strings = split(s, delimiter);
if(!strings.empty()){
std::cout << (strings.size() - 1) << " " << strings.back() << std::endl;
}
C++17 和std::string_view
此外,在 C++17 及以上版本中,std::string_view 也可用。
当std::string_view 创建时,不需要复制数据,它将提供一种性能有效的方法。
因此,在这里我还为std::string 和std::string_view 提出了一个可移植的通用拆分函数。
使用std::string 和std::string_view 几乎相同的ctors,即
basic_string(const charT* s, size_type n,
const Allocator& a = Allocator());
和
constexpr basic_string_view(const charT* str, size_type len);
,并使用通用引用定义以下模板函数
template<typename C>
auto split(C&& str, const std::string& delimiter)
{
std::vector<typename std::remove_reference<C>::type> strings;
for (auto p = str.data(), end = p + str.length();
p != end;
p += ((p==end) ? 0 : delimiter.length()) )
{
const auto pre = p;
p = std::search(pre, end, delimiter.cbegin(), delimiter.cend());
if (p != pre)
{
strings.emplace_back(pre, p - pre);
}
}
return strings;
}
,我们可以通过以下各种方式将s 与delimiter 分开。
DEMO is here.
// std::string_view
const auto strings = split<std::string_view>(s, delimiter);
// std::string(s: lvalue reference)
const auto strings = split(s, delimiter);
// std::string(s: rvalue)
const auto strings = split(std::move(s), delimiter);