一个高效、小巧而优雅的解决方案,使用模板函数实现:
template <class ContainerT>
void split(const std::string& str, ContainerT& tokens,
const std::string& delimiters = " ", bool trimEmpty = false)
{
std::string::size_type pos, lastPos = 0, length = str.length();
using value_type = typename ContainerT::value_type;
using size_type = typename ContainerT::size_type;
while (lastPos < length + 1)
{
pos = str.find_first_of(delimiters, lastPos);
if (pos == std::string::npos)
pos = length;
if (pos != lastPos || !trimEmpty)
tokens.emplace_back(value_type(str.data() + lastPos,
(size_type)pos - lastPos));
lastPos = pos + 1;
}
}
我通常选择使用
std::vector<std::string>
类型作为我的第二个参数(
ContainerT
)... 但是在某些情况下,可能更喜欢使用
list<...>
而不是
vector<...>
。
它还允许您通过最后一个可选参数来指定是否修剪结果中的空标记。
它只需要通过
<string>
包含
std::string
。它不明确使用流或boost库,但可以接受其中一些类型。
此外,自C++-17以来,您可以使用
std::vector<std::string_view>
,它比使用
std::string
更快且更节省内存。以下是修订版本,还支持容器作为返回类型:
#include <vector>
#include <string_view>
#include <utility>
template < typename StringT,
typename DelimiterT = char,
typename ContainerT = std::vector<std::string_view> >
ContainerT split(StringT const& str, DelimiterT const& delimiters = ' ', bool trimEmpty = true, ContainerT&& tokens = {})
{
typename StringT::size_type pos, lastPos = 0, length = str.length();
while (lastPos < length + 1)
{
pos = str.find_first_of(delimiters, lastPos);
if (pos == StringT::npos)
pos = length;
if (pos != lastPos || !trimEmpty)
tokens.emplace_back(str.data() + lastPos, pos - lastPos);
lastPos = pos + 1;
}
return std::forward<ContainerT>(tokens);
}
已经注意到不要制作任何不必要的副本。
这将允许以下两种情况之一:
for (auto const& line : split(str, '\n'))
或者:
auto& lines = split(str, '\n');
返回默认模板容器类型
std::vector<std::string_view>
。
要获取特定的容器类型,或者传递一个现有的容器,请使用
tokens
输入参数,可以是具有类型的初始容器或现有的容器变量:
auto& lines = split(str, '\n', false, std::vector<std::string>())
或者:
std::vector<std::string> lines;
split(str, '\n', false, lines);
string sub; while (iss >> sub) cout << "Substring: " << sub << '\n';
。 - Tony Delroy