使用字符串分隔符(标准 C++)在 C++ 中解析(拆分)字符串

Parse (split) a string in C++ using string delimiter (standard C++)

提问人:TheCrazyProgrammer 提问时间:1/11/2013 最后编辑:plasmacelTheCrazyProgrammer 更新时间:7/3/2023 访问量:2195999

问:

我正在使用以下命令解析 C++ 中的字符串:

using namespace std;

string parsed,input="text to be parsed";
stringstream input_stringstream(input);

if (getline(input_stringstream,parsed,' '))
{
     // do some processing.
}

使用单个字符分隔符进行解析是可以的。但是,如果我想使用字符串作为分隔符怎么办。

示例:我想拆分:

scott>=tiger

作为分隔符,这样我就可以得到斯科特和老虎。>=

C++ 解析 拆分 令牌 tokenize

评论

7赞 Wais Kamal 3/6/2021
stackoverflow.blog/2019/10/11/......向下滚动到 #5。
0赞 AmirSalar 7/20/2021
请参阅此问题:使用 C++20 实现读取文件和拆分字符串。
1赞 Thomas Weller 10/18/2022
@WaisKamal:你可以直接链接到 stackoverflow.com/questions/236129/...

答:

19赞 ryanbwork 1/11/2013 #1

strtok 允许您传入多个字符作为分隔符。我敢打赌,如果您传入“>=”,您的示例字符串将被正确拆分(即使 > 和 = 被计为单独的分隔符)。

编辑 如果您不想用于将字符串转换为 char*,则可以使用 substrfind_first_of 进行标记化。c_str()

string token, mystring("scott>=tiger");
while(token != mystring){
  token = mystring.substr(0,mystring.find_first_of(">="));
  mystring = mystring.substr(mystring.find_first_of(">=") + 1);
  printf("%s ",token.c_str());
}

评论

3赞 TheCrazyProgrammer 1/11/2013
谢谢。但是我只想使用 C++ 而不是任何 C 函数,因为它要求我使用 char 数组而不是字符串。strtok()
2赞 Qix - MONICA WAS MISTREATED 10/14/2016
@TheCrazyProgrammer 所以呢?如果 C 函数可以满足您的需求,请使用它。这不是一个 C++ 中没有 C 函数的世界(事实上,它们必须可用)。 既便宜又容易。.c_str()
1赞 Amber Elferink 8/29/2019
如果字符串中有重复元素,则对 if(token != mystring) 的检查会给出错误的结果。我用你的代码制作了一个没有这个的版本。它有很多变化,从根本上改变了答案,所以我写了自己的答案,而不是编辑。在下面检查。
5赞 Benjamin Lindley 1/11/2013 #2

我会用.以下是解释如何制作适当的分词器函数的文档: http://www.boost.org/doc/libs/1_52_0/libs/tokenizer/tokenizerfunction.htmboost::tokenizer

这是一个适合您的情况的方法。

struct my_tokenizer_func
{
    template<typename It>
    bool operator()(It& next, It end, std::string & tok)
    {
        if (next == end)
            return false;
        char const * del = ">=";
        auto pos = std::search(next, end, del, del + 2);
        tok.assign(next, pos);
        next = pos;
        if (next != end)
            std::advance(next, 2);
        return true;
    }

    void reset() {}
};

int main()
{
    std::string to_be_parsed = "1) one>=2) two>=3) three>=4) four";
    for (auto i : boost::tokenizer<my_tokenizer_func>(to_be_parsed))
        std::cout << i << '\n';
}

评论

7赞 TheCrazyProgrammer 1/11/2013
谢谢。但我只想希望标准的 C++ 而不是第三方库。
0赞 Benjamin Lindley 1/11/2013
@TheCrazyProgrammer:好的,当我读到“标准C++”时,我认为这意味着没有非标准的扩展,并不是说你不能使用符合标准的第三方库。
967赞 Vincenzo Pii 1/11/2013 #3

您可以使用 std::string::find() 函数查找字符串分隔符的位置,然后使用 std::string::substr() 获取令牌。

例:

std::string s = "scott>=tiger";
std::string delimiter = ">=";
std::string token = s.substr(0, s.find(delimiter)); // token is "scott"
  • 该函数返回字符串中第一次出现的位置,如果未找到字符串,则返回 nposfind(const string& str, size_t pos = 0)str

  • 该函数返回对象的子字符串,从 position 开始,长度为 。substr(size_t pos = 0, size_t n = npos)posnpos


如果您有多个分隔符,则在提取一个标记后,可以将其删除(包括分隔符)以继续进行后续提取(如果要保留原始字符串,只需使用 ):s = s.substr(pos + delimiter.length());

s.erase(0, s.find(delimiter) + delimiter.length());

这样,您可以轻松地循环获取每个令牌。

完整示例

std::string s = "scott>=tiger>=mushroom";
std::string delimiter = ">=";

size_t pos = 0;
std::string token;
while ((pos = s.find(delimiter)) != std::string::npos) {
    token = s.substr(0, pos);
    std::cout << token << std::endl;
    s.erase(0, pos + delimiter.length());
}
std::cout << s << std::endl;

输出:

scott
tiger
mushroom

评论

148赞 Hayk Martiros 1/31/2015
对于那些不想修改输入字符串的人,请执行size_t last = 0; size_t next = 0; while ((next = s.find(delimiter, last)) != string::npos) { cout << s.substr(last, next-last) << endl; last = next + 1; } cout << s.substr(last) << endl;
57赞 Don Larynx 2/1/2015
注意:环路外的输出,即mushrooms = mushroom
2赞 rfog 8/17/2015
这些示例不会从字符串中提取最后一个标记。我的一个样本从一个字符串中提取 IpV4:<code>size_t last = 0;size_t下一个 = 0;int 索引 = 0;while (index<4) { next = str.find(delimiter, last); 自动编号 = str.substr(last, next - last);IPv4[索引++] = atoi(number.c_str());最后一个 = 下一个 + 1;}</code>
4赞 ervinbosenbacher 10/30/2015
@hayk.mart 请注意,这将是以下内容,您需要添加 2 而不是 1,因为分隔符的大小是 2 个字符:): std::string s = “scott>=tiger>=mushroom”;std::string 分隔符 = “>=”;size_t最后一个 = 0;size_t下一个 = 0;while ((next = s.find(delimiter, last)) != std::string::npos) { std::cout << s.substr(last, next-last) << std::endl; last = next + 2; } std::cout << s.substr(last) << std::endl;
25赞 Qix - MONICA WAS MISTREATED 9/9/2020
想知道 615 名赞成者中有多少人错过了最后一行,并在他们的生产代码中运行隐藏的错误。从评论来看,我至少敢打赌。IMO 如果它不使用而是将其显示为函数,则此答案将更适合。cout
130赞 moswald 1/11/2013 #4

此方法通过记住上一个子字符串标记的开头和结尾来使用而不更改原始字符串。std::string::find

#include <iostream>
#include <string>

int main()
{
    std::string s = "scott>=tiger";
    std::string delim = ">=";

    auto start = 0U;
    auto end = s.find(delim);
    while (end != std::string::npos)
    {
        std::cout << s.substr(start, end - start) << std::endl;
        start = end + delim.length();
        end = s.find(delim, start);
    }

    std::cout << s.substr(start, end);
}

评论

0赞 Areeb Muzaffar 3/17/2021
如何对 vector<string 执行此操作>其中 vector 中的两个字符串具有相同的形式并具有相同的分隔符。我只想输出两个字符串,就像这适用于一个字符串一样。当然,我的“字符串 delim”将保持不变
0赞 Jonas Wilms 9/12/2021
最后一行不应该是吗?我想这只能这样工作,因此它总是需要字符串的其余部分......s.substr(start, end - start)start + end > size()
1赞 moswald 9/12/2021
因为 ,这意味着我们要返回最终的令牌。end == std::string::npos
2赞 Peng 12/26/2021
最后一行可以进一步简化为无需指定长度,因为如果我们省略长度,它将提取整个训练子字符串。s.substr(start)
0赞 Steve Ward 11/10/2022
你可以进入这个条件。end = s.find(delim, start)while
62赞 Sviatoslav 5/26/2016 #5

您可以使用 next 函数拆分字符串:

vector<string> split(const string& str, const string& delim)
{
    vector<string> tokens;
    size_t prev = 0, pos = 0;
    do
    {
        pos = str.find(delim, prev);
        if (pos == string::npos) pos = str.length();
        string token = str.substr(prev, pos-prev);
        if (!token.empty()) tokens.push_back(token);
        prev = pos + delim.length();
    }
    while (pos < str.length() && prev < str.length());
    return tokens;
}

评论

12赞 kyriakosSt 12/1/2018
IMO 它没有按预期工作:将返回一个向量或单个字符串,我认为如果它返回一个元素向量会更有意义。在 Python 中使用时,对我来说很直观,它应该返回一个空字符串,以防在开头或结尾找到,但这只是我的意见。无论如何,我只是认为应该提及split("abc","a")"bc"["", "bc"]str.split()delim
5赞 Steve 3/13/2019
强烈建议删除防止@kyriakosSt提到的问题以及与连续分隔符相关的其他问题。if (!token.empty())
1赞 bhaller 11/21/2019
如果可以的话,我会删除我的赞成票,但 SO 不会让我。@kyriakosSt提出的问题是一个问题,删除似乎不足以解决它。if (!token.empty())
2赞 Sviatoslav 2/27/2020
@bhaller这个片段的设计正好是为了跳过空片段。如果你需要保留空的,恐怕你需要编写另一个拆分实现。为了社区的利益,请建议您在此处发布。
-5赞 Yilei 2/28/2017 #6
std::vector<std::string> split(const std::string& s, char c) {
  std::vector<std::string> v;
  unsigned int ii = 0;
  unsigned int j = s.find(c);
  while (j < s.length()) {
    v.push_back(s.substr(i, j - i));
    i = ++j;
    j = s.find(c, j);
    if (j >= s.length()) {
      v.push_back(s.substr(i, s,length()));
      break;
    }
  }
  return v;
}

评论

3赞 jstuardo 3/30/2017
请更准确。您的代码将无法编译。请参阅声明“i”和逗号而不是点。
1赞 user2366975 5/23/2017 #7

如果您不想修改字符串(如 Vincenzo Pii 的回答中所示),并且还想输出最后一个标记,则可能需要使用此方法:

inline std::vector<std::string> splitString( const std::string &s, const std::string &delimiter ){
    std::vector<std::string> ret;
    size_t start = 0;
    size_t end = 0;
    size_t len = 0;
    std::string token;
    do{ end = s.find(delimiter,start); 
        len = end - start;
        token = s.substr(start, len);
        ret.emplace_back( token );
        start += len + delimiter.length();
        std::cout << token << std::endl;
    }while ( end != std::string::npos );
    return ret;
}
5赞 Beder Acosta Borges 5/24/2017 #8

这是我对此的看法。它处理边缘情况,并采用可选参数从结果中删除空条目。

bool endsWith(const std::string& s, const std::string& suffix)
{
    return s.size() >= suffix.size() &&
           s.substr(s.size() - suffix.size()) == suffix;
}

std::vector<std::string> split(const std::string& s, const std::string& delimiter, const bool removeEmptyEntries = false)
{
    std::vector<std::string> tokens;

    for (size_t start = 0, end; start < s.length(); start = end + delimiter.length())
    {
         size_t position = s.find(delimiter, start);
         end = position != std::string::npos ? position : s.length();

         std::string token = s.substr(start, end - start);
         if (!removeEmptyEntries || !token.empty())
         {
             tokens.push_back(token);
         }
    }

    if (!removeEmptyEntries &&
        (s.empty() || endsWith(s, delimiter)))
    {
        tokens.push_back("");
    }

    return tokens;
}

例子

split("a-b-c", "-"); // [3]("a","b","c")

split("a--c", "-"); // [3]("a","","c")

split("-b-", "-"); // [3]("","b","")

split("--c--", "-"); // [5]("","","c","","")

split("--c--", "-", true); // [1]("c")

split("a", "-"); // [1]("a")

split("", "-"); // [1]("")

split("", "-", true); // [0]()

评论

0赞 Steve Ward 11/10/2022
C++ 20 向 中添加了成员函数。en.cppreference.com/w/cpp/string/basic_string/ends_withends_withstd::string
22赞 William Cuervo 6/12/2017 #9

此代码将行与文本分开,并将每个人添加到一个向量中。

vector<string> split(char *phrase, string delimiter){
    vector<string> list;
    string s = string(phrase);
    size_t pos = 0;
    string token;
    while ((pos = s.find(delimiter)) != string::npos) {
        token = s.substr(0, pos);
        list.push_back(token);
        s.erase(0, pos + delimiter.length());
    }
    list.push_back(s);
    return list;
}

调用人:

vector<string> listFilesMax = split(buffer, "\n");

评论

0赞 Stoica Mircea 5/25/2018
效果很好!我添加了list.push_back;因为它不见了。
1赞 whihathac 6/1/2018
它错过了字符串的最后一部分。在 while 循环结束后,我们需要添加剩余的 s 作为新标记。
0赞 fret 11/14/2018
我对代码示例进行了编辑以修复缺少的push_back。
1赞 Mayur 5/17/2019
它会更好vector<string> split(char *phrase, const string delimiter="\n")
1赞 squ1dd13 11/12/2020
@OliverTworkowski很多时候,被视为“正确”的行为涉及将空字符串留在里面。当然,这在您的用例中可能是不可取的,在这种情况下,您的建议是完全有效的。
155赞 Arafat Hasan 10/25/2017 #10

对于字符串分隔符

根据字符串分隔符拆分字符串。比如基于字符串分隔符拆分字符串,输出会"adsf-+qwret-+nvfkbdsj-+orthdfjgh-+dfjrleih""-+"{"adsf", "qwret", "nvfkbdsj", "orthdfjgh", "dfjrleih"}

#include <iostream>
#include <sstream>
#include <vector>

// for string delimiter
std::vector<std::string> split(std::string s, std::string delimiter) {
    size_t pos_start = 0, pos_end, delim_len = delimiter.length();
    std::string token;
    std::vector<std::string> res;

    while ((pos_end = s.find(delimiter, pos_start)) != std::string::npos) {
        token = s.substr (pos_start, pos_end - pos_start);
        pos_start = pos_end + delim_len;
        res.push_back (token);
    }

    res.push_back (s.substr (pos_start));
    return res;
}

int main() {
    std::string str = "adsf-+qwret-+nvfkbdsj-+orthdfjgh-+dfjrleih";
    std::string delimiter = "-+";
    std::vector<std::string> v = split (str, delimiter);

    for (auto i : v) cout << i << endl;

    return 0;
}

**输出**
adsf
qwret
nvfkbdsj
orthdfjgh
dfjrleih




对于单字符分隔符

根据字符分隔符拆分字符串。例如,使用分隔符拆分字符串将输出"adsf+qwer+poui+fdgh""+"{"adsf", "qwer", "poui", "fdgh"}

#include <iostream>
#include <sstream>
#include <vector>

std::vector<std::string> split (const std::string &s, char delim) {
    std::vector<std::string> result;
    std::stringstream ss (s);
    std::string item;

    while (getline (ss, item, delim)) {
        result.push_back (item);
    }

    return result;
}

int main() {
    std::string str = "adsf+qwer+poui+fdgh";
    std::vector<std::string> v = split (str, '+');

    for (auto i : v) cout << i << endl;

    return 0;
}

**输出**
adsf
qwer
poui
fdgh

评论

0赞 Mayur 11/27/2018
你回来了,我认为它会调用复制构造函数。vector<string>
8赞 David Given 1/12/2019
我看到的每个引用都表明,在该上下文中消除了对复制构造函数的调用。
1赞 Kevin 3/13/2019
对于“现代”(C++?)编译器,我相信这是正确的,RVO和/或移动语义将消除复制构造函数。
3赞 kounoupis 3/26/2019
我尝试了单字符分隔符,如果字符串以分隔符结尾(即行尾的空 csv 列),它不会返回空字符串。它只是少返回一个字符串。例如:1,2,3,4\nA,B,C,
3赞 kounoupis 3/26/2019
我还尝试了字符串分隔符,如果字符串以分隔符结尾,则最后一个分隔符将成为提取的最后一个字符串的一部分。
0赞 user5337845 1/29/2018 #11
#include<iostream>
#include<algorithm>
using namespace std;

int split_count(string str,char delimit){
return count(str.begin(),str.end(),delimit);
}

void split(string str,char delimit,string res[]){
int a=0,i=0;
while(a<str.size()){
res[i]=str.substr(a,str.find(delimit));
a+=res[i].size()+1;
i++;
}
}

int main(){

string a="abc.xyz.mno.def";
int x=split_count(a,'.')+1;
string res[x];
split(a,'.',res);

for(int i=0;i<x;i++)
cout<<res[i]<<endl;
  return 0;
}

PS:只有当拆分后字符串的长度相等时才有效

评论

1赞 user202729 4/10/2018
这使用 GCC 扩展 -- 可变长度数组。
3赞 Amber Elferink 7/11/2019 #12

这是一个完整的方法,它拆分任何分隔符上的字符串并返回切碎字符串的向量。

这是对 ryanbwork 答案的改编。但是,如果您的字符串中有重复元素,他的检查 for: 会给出错误的结果。这是我对这个问题的解决方案。if(token != mystring)

vector<string> Split(string mystring, string delimiter)
{
    vector<string> subStringList;
    string token;
    while (true)
    {
        size_t findfirst = mystring.find(delimiter);
        if (findfirst == string::npos) //find returns npos if it couldn't find the delimiter anymore
        {
            subStringList.push_back(mystring); //push back the final piece of mystring
            return subStringList;
        }
        token = mystring.substr(0, mystring.find(delimiter));
        mystring = mystring.substr(mystring.find(delimiter) + delimiter.size());
        subStringList.push_back(token);
    }
    return subStringList;
}

评论

2赞 Joel Bodenmann 12/5/2019
在这样的代码段中看到类似的东西通常是可怕的。就我个人而言,我建议重写它,以便与 进行比较(或分别对 进行检查)使 过时。while (true)std::string::nposmystring.size()while (true)
1赞 Steve Ward 11/10/2022
重复分配效率低下。您可以将起始索引传递给 。此外,每次迭代都要调用 3 次。mystringfind_first_offind_first_of
0赞 Кое Кто 6/20/2023
我不确定它是否适用于多字符分隔符。因为:“查找与给定字符序列中的某个字符相等的第一个字符。find_first_of
1赞 Amber Elferink 6/21/2023
我认为你是对@КоеКто。我使用一些基本示例调整和测试了代码,以适用于多个分隔符。
21赞 Shubham Agrawal 8/4/2019 #13

答案已经存在,但 selected-answer 使用擦除功能,这非常昂贵,想想一些非常大的字符串(以 MB 为单位)。因此,我使用以下函数。

vector<string> split(const string& str, const string& delim)
{
    vector<string> result;
    size_t start = 0;

    for (size_t found = str.find(delim); found != string::npos; found = str.find(delim, start))
    {
        result.emplace_back(str.begin() + start, str.begin() + found);
        start = found + delim.size();
    }
    if (start != str.size())
        result.emplace_back(str.begin() + start, str.end());
    return result;      
}

评论

2赞 Robbie Capps 4/29/2020
我测试了这一点,它有效。谢谢!在我看来,这是最好的答案,因为正如原始答案所述,该解决方案减少了内存开销,并且结果可以方便地存储在向量中。(复制 Python 方法。string.split()
0赞 jezza 8/15/2022
一个不错的改进是使用而不是emplace_back()push_back(string(...))
0赞 Shubham Agrawal 8/18/2022
@jezza很荣幸。
0赞 jezza 8/19/2022
您也可以删除对字符串构造函数的显式调用。 将其参数转发给构造函数,以便您可以编写 .emplace_back()result.emplace_back(i_str.begin()+startIndex, i_str.begin()+found);
0赞 Steve Ward 11/10/2022
您可以移动到条件范围内,以避免在 2 个地方打电话。found = i_str.find(i_delim, startIndex)whilefind
5赞 hmofrad 11/7/2019 #14

这应该适用于字符串(或单个字符)分隔符。不要忘记包括 .#include <sstream>

std::string input = "Alfa=,+Bravo=,+Charlie=,+Delta";
std::string delimiter = "=,+"; 
std::istringstream ss(input);
std::string token;
std::string::iterator it;

while(std::getline(ss, token, *(it = delimiter.begin()))) {
    std::cout << token << std::endl; // Token is extracted using '='
    it++;
    // Skip the rest of delimiter if exists ",+"
    while(it != delimiter.end() and ss.peek() == *(it)) { 
        it++; ss.get(); 
    }
}

第一个 while 循环使用字符串分隔符的第一个字符提取令牌。第二个 while 循环跳过分隔符的其余部分,并在下一个标记的开头停止。

评论

0赞 Amitoj 3/9/2021
这是不正确的。如果按如下方式修改输入,则当不应使用以下输入时,它将使用第一个 = 进行拆分:std::string input = "Alfa=,+Bravo=,+Charlie=,+Delta=Echo";
0赞 hmofrad 3/10/2021
@Amitoj 好渔获。我修改了我的答案,甚至涵盖了带有畸形分隔符的输入。
0赞 sea-kg 3/14/2020 #15

功能:

std::vector<std::string> WSJCppCore::split(const std::string& sWhat, const std::string& sDelim) {
    std::vector<std::string> vRet;
    size_t nPos = 0;
    size_t nLen = sWhat.length();
    size_t nDelimLen = sDelim.length();
    while (nPos < nLen) {
        std::size_t nFoundPos = sWhat.find(sDelim, nPos);
        if (nFoundPos != std::string::npos) {
            std::string sToken = sWhat.substr(nPos, nFoundPos - nPos);
            vRet.push_back(sToken);
            nPos = nFoundPos + nDelimLen;
            if (nFoundPos + nDelimLen == nLen) { // last delimiter
                vRet.push_back("");
            }
        } else {
            std::string sToken = sWhat.substr(nPos, nLen - nPos);
            vRet.push_back(sToken);
            break;
        }
    }
    return vRet;
}

单元测试:

bool UnitTestSplit::run() {
bool bTestSuccess = true;

    struct LTest {
        LTest(
            const std::string &sStr,
            const std::string &sDelim,
            const std::vector<std::string> &vExpectedVector
        ) {
            this->sStr = sStr;
            this->sDelim = sDelim;
            this->vExpectedVector = vExpectedVector;
        };
        std::string sStr;
        std::string sDelim;
        std::vector<std::string> vExpectedVector;
    };
    std::vector<LTest> tests;
    tests.push_back(LTest("1 2 3 4 5", " ", {"1", "2", "3", "4", "5"}));
    tests.push_back(LTest("|1f|2п|3%^|44354|5kdasjfdre|2", "|", {"", "1f", "2п", "3%^", "44354", "5kdasjfdre", "2"}));
    tests.push_back(LTest("|1f|2п|3%^|44354|5kdasjfdre|", "|", {"", "1f", "2п", "3%^", "44354", "5kdasjfdre", ""}));
    tests.push_back(LTest("some1 => some2 => some3", "=>", {"some1 ", " some2 ", " some3"}));
    tests.push_back(LTest("some1 => some2 => some3 =>", "=>", {"some1 ", " some2 ", " some3 ", ""}));

    for (int i = 0; i < tests.size(); i++) {
        LTest test = tests[i];
        std::string sPrefix = "test" + std::to_string(i) + "(\"" + test.sStr + "\")";
        std::vector<std::string> vSplitted = WSJCppCore::split(test.sStr, test.sDelim);
        compareN(bTestSuccess, sPrefix + ": size", vSplitted.size(), test.vExpectedVector.size());
        int nMin = std::min(vSplitted.size(), test.vExpectedVector.size());
        for (int n = 0; n < nMin; n++) {
            compareS(bTestSuccess, sPrefix + ", element: " + std::to_string(n), vSplitted[n], test.vExpectedVector[n]);
        }
    }

    return bTestSuccess;
}
0赞 XLVII 5/28/2020 #16
std::vector<std::string> parse(std::string str,std::string delim){
    std::vector<std::string> tokens;
    char *str_c = strdup(str.c_str()); 
    char* token = NULL;

    token = strtok(str_c, delim.c_str()); 
    while (token != NULL) { 
        tokens.push_back(std::string(token));  
        token = strtok(NULL, delim.c_str()); 
    }

    delete[] str_c;

    return tokens;
}

评论

1赞 jezza 8/15/2022
由于您是按值传递的,因此无需调用 .这也是 而不是 的正确用例。strstrdupemplace_back()push_back
0赞 Steve Ward 11/10/2022
strdup使用 获取内存。但是你用 .mallocdelete[]
5赞 Shubham Kumar Gupta 9/7/2020 #17

一个非常简单/幼稚的方法:

vector<string> words_seperate(string s){
    vector<string> ans;
    string w="";
    for(auto i:s){
        if(i==' '){
           ans.push_back(w);
           w="";
        }
        else{
           w+=i;
        }
    }
    ans.push_back(w);
    return ans;
}

或者你可以使用 boost 库拆分功能:

vector<string> result; 
boost::split(result, input, boost::is_any_of("\t"));

或者你可以试试 TOKEN 或 strtok:

char str[] = "DELIMIT-ME-C++"; 
char *token = strtok(str, "-"); 
while (token) 
{ 
    cout<<token; 
    token = strtok(NULL, "-"); 
} 

或者你可以这样做:

char split_with=' ';
vector<string> words;
string token; 
stringstream ss(our_string);
while(getline(ss , token , split_with)) words.push_back(token);
2赞 cdahms 10/10/2020 #18

由于这是最受好评的 Stack Overflow Google 搜索结果或类似结果,我将发布一个完整的、可复制/粘贴的可运行示例,展示这两种方法。C++ split string

splitString用途(在大多数情况下可能是更好、更简单的选择)stringstream

splitString2使用和(更手动的方法)findsubstr

// SplitString.cpp

#include <iostream>
#include <vector>
#include <string>
#include <sstream>

// function prototypes
std::vector<std::string> splitString(const std::string& str, char delim);
std::vector<std::string> splitString2(const std::string& str, char delim);
std::string getSubstring(const std::string& str, int leftIdx, int rightIdx);


int main(void)
{
  // Test cases - all will pass
  
  std::string str = "ab,cd,ef";
  //std::string str = "abcdef";
  //std::string str = "";
  //std::string str = ",cd,ef";
  //std::string str = "ab,cd,";   // behavior of splitString and splitString2 is different for this final case only, if this case matters to you choose which one you need as applicable
  
  
  std::vector<std::string> tokens = splitString(str, ',');
  
  std::cout << "tokens: " << "\n";
  
  if (tokens.empty())
  {
    std::cout << "(tokens is empty)" << "\n";
  }
  else
  {
    for (auto& token : tokens)
    {
      if (token == "") std::cout << "(empty string)" << "\n";
      else std::cout << token << "\n";
    }
  }
    
  return 0;
}

std::vector<std::string> splitString(const std::string& str, char delim)
{
  std::vector<std::string> tokens;
  
  if (str == "") return tokens;
  
  std::string currentToken;
  
  std::stringstream ss(str);
  
  while (std::getline(ss, currentToken, delim))
  {
    tokens.push_back(currentToken);
  }
  
  return tokens;
}

std::vector<std::string> splitString2(const std::string& str, char delim)
{
  std::vector<std::string> tokens;
  
  if (str == "") return tokens;
  
  int leftIdx = 0;
  
  int delimIdx = str.find(delim);
  
  int rightIdx;
  
  while (delimIdx != std::string::npos)
  {
    rightIdx = delimIdx - 1;
    
    std::string token = getSubstring(str, leftIdx, rightIdx);
    tokens.push_back(token);
    
    // prep for next time around
    leftIdx = delimIdx + 1;
    
    delimIdx = str.find(delim, delimIdx + 1);
  }
  
  rightIdx = str.size() - 1;
  
  std::string token = getSubstring(str, leftIdx, rightIdx);
  tokens.push_back(token);
  
  return tokens;
}

std::string getSubstring(const std::string& str, int leftIdx, int rightIdx)
{
  return str.substr(leftIdx, rightIdx - leftIdx + 1);
}
-1赞 Magix 11/12/2020 #19

作为奖励,这里有一个易于使用的拆分函数和宏的代码示例,您可以在其中选择容器类型:

#include <iostream>
#include <vector>
#include <string>

#define split(str, delim, type) (split_fn<type<std::string>>(str, delim))
 
template <typename Container>
Container split_fn(const std::string& str, char delim = ' ') {
    Container cont{};
    std::size_t current, previous = 0;
    current = str.find(delim);
    while (current != std::string::npos) {
        cont.push_back(str.substr(previous, current - previous));
        previous = current + 1;
        current = str.find(delim, previous);
    }
    cont.push_back(str.substr(previous, current - previous));
    
    return cont;
}

int main() {
    
    auto test = std::string{"This is a great test"};
    auto res = split(test, ' ', std::vector);
    
    for(auto &i : res) {
        std::cout << i << ", "; // "this", "is", "a", "great", "test"
    }
    
    
    return 0;
}
44赞 Hossein 11/18/2020 #20

您也可以为此使用正则表达式:

std::vector<std::string> split(const std::string str, const std::string regex_str)
{
    std::regex regexz(regex_str);
    std::vector<std::string> list(std::sregex_token_iterator(str.begin(), str.end(), regexz, -1),
                                  std::sregex_token_iterator());
    return list;
}

这相当于:

std::vector<std::string> split(const std::string str, const std::string regex_str)
{
    std::sregex_token_iterator token_iter(str.begin(), str.end(), regexz, -1);
    std::sregex_token_iterator end;
    std::vector<std::string> list;
    while (token_iter != end)
    {
        list.emplace_back(*token_iter++);
    }
    return list;
}

并像这样使用它:

#include <iostream>
#include <string>
#include <regex>

std::vector<std::string> split(const std::string str,
                               const std::string regex_str) {
    std::regex regexz(regex_str);
    return {std::sregex_token_iterator(str.begin(), str.end(), regexz, -1),
            std::sregex_token_iterator()};
}

int main()
{
    std::string input_str = "lets split this";
    std::string regex_str = " "; 
    auto tokens = split(input_str, regex_str);
    for (auto& item: tokens)
    {
        std::cout<<item <<std::endl;
    }
}

在线玩吧!

您可以像正常一样简单地使用子字符串、字符等,或者使用实际的正则表达式进行拆分。
它也简洁明了,C++11!

评论

2赞 DeusXMachina 1/9/2021
这应该是正确的答案,前提是 C++11 在桌面上,如果不是......您应该使用 C++>=11,这是一个游戏规则改变者!
0赞 BFamz 2/3/2021
请您解释一下函数中的return语句吗?我正在尝试弄清楚令牌是如何被推送到容器中的。谢谢。split()std::vector
8赞 pob 3/25/2021
@DeusXMachina:当然,这是一个很好的解决方案。需要注意的是:最后一个代码段中的“更简洁的形式”不会与_LIBCPP_STD_VER > 11一起编译,因为该方法被标记为“删除”...但是早期的代码段不隐式要求右值引用和编译,并在 C++2a 下运行良好。
0赞 Tom Sirgedas 6/4/2021
对于大型案例来说,这似乎很慢。否则非常好。
0赞 PLG 8/14/2022
我建议在序列中遇到多个空格时避免使用空字符串。std::string regex_str= "\\s+"
-1赞 luca 2/10/2021 #21

从 C++11 开始,它可以像这样完成:

std::vector<std::string> splitString(const std::string& str,
                                     const std::regex& regex)
{
  return {std::sregex_token_iterator{str.begin(), str.end(), regex, -1}, 
          std::sregex_token_iterator() };
} 

// usually we have a predefined set of regular expressions: then
// let's build those only once and re-use them multiple times
static const std::regex regex1(R"some-reg-exp1", std::regex::optimize);
static const std::regex regex2(R"some-reg-exp2", std::regex::optimize);
static const std::regex regex3(R"some-reg-exp3", std::regex::optimize);

string str = "some string to split";
std::vector<std::string> tokens( splitString(str, regex1) ); 

笔记:

评论

2赞 not2qubit 5/19/2021
这是一个不完整的答案,没有真正做或解释任何事情。
0赞 Greck 2/25/2021 #22
template<typename C, typename T>
auto insert_in_container(C& c, T&& t) -> decltype(c.push_back(std::forward<T>(t)), void()) {
    c.push_back(std::forward<T>(t));
}
template<typename C, typename T>
auto insert_in_container(C& c, T&& t) -> decltype(c.insert(std::forward<T>(t)), void()) {
    c.insert(std::forward<T>(t));
}
template<typename Container>
Container splitR(const std::string& input, const std::string& delims) {
    Container out;
    size_t delims_len = delims.size();
    auto begIdx = 0u;
    auto endIdx = input.find(delims, begIdx);
    if (endIdx == std::string::npos && input.size() != 0u) {
        insert_in_container(out, input);
    }
    else {
        size_t w = 0;
        while (endIdx != std::string::npos) {
            w = endIdx - begIdx;
            if (w != 0) insert_in_container(out, input.substr(begIdx, w));
            begIdx = endIdx + delims_len;
            endIdx = input.find(delims, begIdx);
        }
        w = input.length() - begIdx;
        if (w != 0) insert_in_container(out, input.substr(begIdx, w));
    }
    return out;
}
2赞 SridharKritha 3/3/2021 #23

另一个答案:这里我使用字符串函数,它返回第一个字符的位置,该字符与 delim 中指定的任何字符都匹配。find_first_not_of

size_t find_first_not_of(const string& delim, size_t pos = 0) const noexcept;

例:

int main()
{
    size_t start = 0, end = 0;
    std::string str = "scott>=tiger>=cat";
    std::string delim = ">=";
    while ((start = str.find_first_not_of(delim, end)) != std::string::npos)
    {
        end = str.find(delim, start); // finds the 'first' occurance from the 'start'
        std::cout << str.substr(start, end - start)<<std::endl; // extract substring
    }
    return 0;
}

输出:

    scott
    tiger
    cat
3赞 jacob galam 3/9/2021 #24

我做这个解决方案。这很简单,所有打印/值都在循环中(循环后无需检查)。

#include <iostream>
#include <string>

using std::cout;
using std::string;

int main() {
    string s = "it-+is-+working!";
    string d = "-+";

    int firstFindI = 0;
    int secendFindI = 0;
    while (secendFindI != string::npos)
    {
        secendFindI = s.find(d, firstFindI);
        cout << s.substr(firstFindI, secendFindI - firstFindI) << "\n"; // print sliced part
        firstFindI = secendFindI + d.size(); // add to the search index
    }
}

感谢 @SteveWard 改进了这个答案。

评论

0赞 Steve Ward 11/11/2022
如果使用 do/while 循环,则无需调用两次。s.find
0赞 Radem 5/8/2021 #25

我使用指针算术。内侧,而对于字符串分界线,如果您用 char delim 满足,只需简单地删除内侧即可。我希望它是正确的。如果您发现任何错误或改进,请发表评论。

std::vector<std::string> split(std::string s, std::string delim)
{
    char *p = &s[0];
    char *d = &delim[0];
    std::vector<std::string> res = {""};

    do
    {
        bool is_delim = true;
        char *pp = p;
        char *dd = d;
        while (*dd && is_delim == true)
            if (*pp++ != *dd++)
                is_delim = false;

        if (is_delim)
        {
            p = pp - 1;
            res.push_back("");
        }
        else
            *(res.rbegin()) += *p;
    } while (*p++);

    return res;
}

评论

0赞 Pawara Siriwardhane 5/8/2021
欢迎使用 Stack Overflow。虽然这段代码可能会解决这个问题,但包括解释它如何以及为什么解决这个问题将真正有助于提高你的帖子的质量,并可能导致更多的赞成票。请记住,您是在为将来的读者回答问题,而不仅仅是现在提问的人。请编辑您的答案以添加解释,并指出适用的限制和假设。
1赞 JadeSpy 6/8/2021 #26

这是一个简洁的拆分函数。我决定让背靠背分隔符作为空字符串返回,但如果子字符串为空,您可以轻松检查,如果是空,则不要将其添加到向量中。

#include <vector>
#include <string>
using namespace std;



vector<string> split(string to_split, string delimiter) {
    size_t pos = 0;
    vector<string> matches{};
    do {
        pos = to_split.find(delimiter);
        int change_end;
        if (pos == string::npos) {
            pos = to_split.length() - 1;
            change_end = 1;
        }
        else {
            change_end = 0;
        }
        matches.push_back(to_split.substr(0, pos+change_end));
        
        to_split.erase(0, pos+1);

    }
    while (!to_split.empty());
    return matches;

}
51赞 Nox 6/25/2021 #27

使用 C++20 实现此目的的方法:

#include <iostream>
#include <ranges>
#include <string_view>

int main()
{
    std::string hello = "text to be parsed";
    auto split = hello
        | std::ranges::views::split(' ')
        | std::ranges::views::transform([](auto&& str) { return std::string_view(&*str.begin(), std::ranges::distance(str)); });

    for (auto&& word : split)
    {
        std::cout << word << std::endl;
    }
}

请参见:
https://stackoverflow.com/a/48403210/10771848
https://en.cppreference.com/w/cpp/ranges/split_view

评论

9赞 Patrick Bassut 4/27/2022
哦,哇。这有点复杂。
0赞 Zitrax 10/20/2022
对于 gcc,您似乎需要版本 10 或更高版本,对于 Clang,它甚至不适用于截至现在的最新版本 (15),但是当前的主干版本可以工作。请参见 godbolt.org/z/a6fEGYo16 。可能是 clang: github.com/llvm/llvm-project/issues/52696 中的这个问题。
0赞 BitTickler 12/12/2022
简单的事情做成了复杂的:)Common Lisp(带有 cl-ppcre 库):.但有些人喜欢它使:)复杂化(defvar *delimiters* (cl-ppcre:create-scanner " ")) (cl-ppcre:split *delimiters* "lets see if it works")
1赞 Matt Eding 7/24/2023
值得庆幸的是,C++ 添加了一个构造函数重载,以使其更简单。std::string_view
3赞 Robert Russell 8/10/2021 #28

这与其他答案类似,但它使用的是 .所以这些只是原始字符串的视图。类似于 c++20 示例。虽然这将是一个 c++17 示例。(编辑以跳过空匹配项)string_view

#include <algorithm>
#include <iostream>
#include <string_view>
#include <vector>
std::vector<std::string_view> split(std::string_view buffer,
                                    const std::string_view delimeter = " ") {
  std::vector<std::string_view> ret{};
  std::decay_t<decltype(std::string_view::npos)> pos{};
  while ((pos = buffer.find(delimeter)) != std::string_view::npos) {
    const auto match = buffer.substr(0, pos);
    if (!match.empty()) ret.push_back(match);
    buffer = buffer.substr(pos + delimeter.size());
  }
  if (!buffer.empty()) ret.push_back(buffer);
  return ret;
}
int main() {
  const auto split_values = split("1 2 3 4 5 6 7 8 9     10 ");
  std::for_each(split_values.begin(), split_values.end(),
                [](const auto& str) { std::cout << str << '\n'; });
  return split_values.size();
}

评论

0赞 Steve Ward 11/10/2022
您是按值传递的,因此向量中的string_views引用临时对象。buffer
0赞 Robert Russell 11/11/2022
是的,这就是string_views的重点。如果你让原始值噗噗一声,那么它就指向垃圾。在此示例中,我使用的是字符串文字。因此,这将在程序的整个生命周期中始终存在。如果要制作永久副本,则可以改用 std::string。我在这里只使用 std::vector,因为我不知道我们会得到多少结果。也许我们可以在 c++23 中使用一个 std 视图,这样我们就可以以更懒惰的方式获得结果。
0赞 Кое Кто 6/20/2023
@SteveWard。我认为这没关系,但仅适用于字符串文字,如本例所示。因为这样的文字实际上不是临时的,而是相当静态的常量。我同意string_view的临时字符串会可疑地悬空。
5赞 KuhakuPixel 9/17/2021 #29

以防万一将来有人想要开箱即用的功能 Vincenzo Pii 的答案

#include <vector>
#include <string>


std::vector<std::string> SplitString(
    std::string str,
    std::string delimeter)
{
    std::vector<std::string> splittedStrings = {};
    size_t pos = 0;

    while ((pos = str.find(delimeter)) != std::string::npos)
    {
        std::string token = str.substr(0, pos);
        if (token.length() > 0)
            splittedStrings.push_back(token);
        str.erase(0, pos + delimeter.length());
    }

    if (str.length() > 0)
        splittedStrings.push_back(str);
    return splittedStrings;
}

我还修复了一些错误,以便在字符串的开头或结尾有分隔符时,函数不会返回空字符串

0赞 markroxor 7/29/2022 #30

一个更简单的解决方案是——

您可以使用 multichar 分隔符进行分隔。 请记住使用,以便原始字符串不会发生突变。strtokstrdup

#include <stdio.h>
#include <string.h>
const char* str = "scott>=tiger";
char *token = strtok(strdup(str), ">=");
while (token != NULL)
    {
        printf("%s\n", token);
        token = strtok(NULL, ">=");
    }

评论

0赞 jezza 8/15/2022
如果你有一个字符串(OP 有),那么使用它的复制构造函数复制它将避免对 strlen 的潜在昂贵调用。当然,既然 C++11 已经存在了 11 年,我们也可以切换到使用 nullptr 而不是 NULL?
1赞 kfc 8/22/2022 #31

此方法使用字符串 find字符串 substr

vector<string> split(const string& str,const string delim){
vector<string> vtokens; 
size_t start = 0;
size_t end = 0;
while((end = str.find(delim,start))!=string::npos){
    vtokens.push_back(str.substr(start,end-start));
    start = end +1;
}
vtokens.push_back(str.substr(start));
return vtokens;
}

评论

1赞 Steve Ward 11/10/2022
如果长度超过 1 个字符,这样做会更有效。start = end + delim.size();delim
0赞 Кое Кто 6/20/2023
@SteveWard我认为这甚至是不正确的,因为 start 用于字符串的开头。
0赞 Calmarius 9/16/2022 #32

我浏览了答案,还没有看到一个可以输入到范围循环中的基于迭代器的方法,所以我做了一个。

这使用 C++17 string_views,因此不应分配字符串的副本。

struct StringSplit
{
    struct Iterator
    {
        size_t tokenStart_ = 0;
        size_t tokenEnd_ = 0;
        std::string str_;
        std::string_view view_;
        std::string delimiter_;
        bool done_ = false;

        Iterator()
        {
            // End iterator.
            done_ = true;
        }

        Iterator(std::string str, std::string delimiter)
            : str_{std::move(str)}, view_{str_}, delimiter_{
                                                     std::move(delimiter)}
        {
            tokenEnd_ = view_.find(delimiter_, tokenStart_);
        }

        std::string_view operator*()
        {
            return view_.substr(tokenStart_, tokenEnd_ - tokenStart_);
        }

        Iterator &operator++()
        {
            if (tokenEnd_ == std::string::npos)
            {
                done_ = true;
                return *this;
            }

            tokenStart_ = tokenEnd_ + delimiter_.size();
            tokenEnd_ = view_.find(delimiter_, tokenStart_);
            return *this;
        }

        bool operator!=(Iterator &other)
        {
            // We only check if both points to the end.
            if (done_ && other.done_)
            {
                return false;
            }

            return true;
        }
    };

    Iterator beginIter_;

    StringSplit(std::string str, std::string delim)
        : beginIter_{std::move(str), std::move(delim)}
    {
    }

    Iterator begin()
    {
        return beginIter_;
    }

    Iterator end()
    {
        return Iterator{};
    }
};

示例用法如下:

int main()
{
    for (auto token : StringSplit{"<>foo<>bar<><>bar<><>baz<><>", "<>"})
    {
        std::cout << "TOKEN: '" << token << "'" << std::endl;
    }
}

打印:

TOKEN: ''
TOKEN: 'foo'
TOKEN: 'bar'
TOKEN: ''
TOKEN: 'bar'
TOKEN: ''
TOKEN: 'baz'
TOKEN: ''
TOKEN: ''

它正确处理字符串开头和结尾的空条目。

0赞 mloskot 2/4/2023 #33

下面是使用 Boost String Algorithms 库和 Boost Range 库将一个字符串与另一个字符串拆分的示例。该解决方案的灵感来自 StringAlgo 库文档中的(适度)建议,请参阅拆分部分。

下面是一个完整的程序,具有功能以及全面的测试 - 尝试使用godbolt:split_with_string

#include <iostream>
#include <string>
#include <vector>
#include <boost/algorithm/string.hpp>
#include <boost/range/iterator_range.hpp>

std::vector<std::string> split_with_string(std::string_view s, std::string_view search) 
{
    if (search.empty()) return {std::string{s}};

    std::vector<boost::iterator_range<std::string_view::iterator>> found;
    boost::algorithm::ifind_all(found, s, search);
    if (found.empty()) return {};

    std::vector<std::string> parts;
    parts.reserve(found.size() + 2); // a bit more

    std::string_view::iterator part_begin = s.cbegin(), part_end;
    for (auto& split_found : found)
    {
        // do not skip empty extracts
        part_end = split_found.begin();
        parts.emplace_back(part_begin, part_end);
        part_begin = split_found.end();
    }
    if (part_end != s.end())
        parts.emplace_back(part_begin, s.end());

    return parts;
}

#define TEST(expr) std::cout << ((!(expr)) ? "FAIL" : "PASS") << ": " #expr "\t" << std::endl

int main()
{
    auto s0 = split_with_string("adsf-+qwret-+nvfkbdsj", "");
    TEST(s0.size() == 1);
    TEST(s0.front() == "adsf-+qwret-+nvfkbdsj");
    auto s1 = split_with_string("adsf-+qwret-+nvfkbdsj", "-+");
    TEST(s1.size() == 3);
    TEST(s1.front() == "adsf");
    TEST(s1.back() == "nvfkbdsj");
    auto s2 = split_with_string("-+adsf-+qwret-+nvfkbdsj-+", "-+");
    TEST(s2.size() == 5);
    TEST(s2.front() == "");
    TEST(s2.back() == "");
    auto s3 = split_with_string("-+adsf-+qwret-+nvfkbdsj", "-+");
    TEST(s3.size() == 4);
    TEST(s3.front() == "");
    TEST(s3.back() == "nvfkbdsj");
    auto s4 = split_with_string("adsf-+qwret-+nvfkbdsj-+", "-+");
    TEST(s4.size() == 4);
    TEST(s4.front() == "adsf");
    TEST(s4.back() == "");
    auto s5 = split_with_string("dbo.abc", "dbo.");
    TEST(s5.size() == 2);
    TEST(s5.front() == "");
    TEST(s5.back() == "abc");
    auto s6 = split_with_string("dbo.abc", ".");
    TEST(s6.size() == 2);
    TEST(s6.front() == "dbo");
    TEST(s6.back() == "abc");
}

测试输出:

PASS: s0.size() == 1    
PASS: s0.front() == "adsf-+qwret-+nvfkbdsj" 
PASS: s1.size() == 3    
PASS: s1.front() == "adsf"  
PASS: s1.back() == "nvfkbdsj"   
PASS: s2.size() == 5    
PASS: s2.front() == ""  
PASS: s2.back() == ""   
PASS: s3.size() == 4    
PASS: s3.front() == ""  
PASS: s3.back() == "nvfkbdsj"   
PASS: s4.size() == 4    
PASS: s4.front() == "adsf"  
PASS: s4.back() == ""   
PASS: s5.size() == 2    
PASS: s5.front() == ""  
PASS: s5.back() == "abc"    
PASS: s6.size() == 2    
PASS: s6.front() == "dbo"   
PASS: s6.back() == "abc"    
0赞 Stefan_l_01 5/24/2023 #34

有些答案缺乏特例。如果你有一个 csv,你想读取相等数量的列,那么代码会在如下情况下失败: 第 1 行:a、b、c、d 第 2 行:g,e,, 对于 Row2,仅读取 3 个项目

循环末尾的特殊处理会添加一个空字符串:

if (startIndex != str.size())
    result.emplace_back(str.begin() + startIndex, str.end());  
else if (result.size())     // min 1 separator found before. 
    result.emplace_back();

但是,如果只有 1 列没有 delim,则它不会添加字符串,该列在某些行中填充了数据,而在其他行中为空

0赞 BuvinJ 6/26/2023 #35

又一个....随着时间的推移,这个应该很容易在不更改函数签名的情况下添加功能,因为我使用了“标志”而不是单独的布尔选项。

utils.h

#include <string>
#include <vector>

namespace utils
{
    void ltrim( std::string &s );
    void rtrim( std::string &s );
    void trim(  std::string &s );
    
    enum SplitFlags
    {
        SPLIT_TRIMMED  = 0x01
    ,   SPLIT_NO_EMPTY = 0x02
    };
    std::vector<std::string> split(
        const std::string &s, const char delimiter, const int flags=0 );
}

utils.cpp

#include <sstream>
#include <algorithm>
#include <cctype>
#include <locale>

#include "utils.h"

void utils::ltrim( std::string &s )
{
    s.erase( s.begin(), std::find_if( s.begin(), s.end(),
        []( unsigned char ch ) { return !std::isspace( ch ); } ) );
}

void utils::rtrim( std::string &s )
{
    s.erase( std::find_if( s.rbegin(), s.rend(),
        []( unsigned char ch ) { return !std::isspace( ch ); } ).base(), s.end() );
}

void utils::trim( std::string &s )
{
    rtrim( s );
    ltrim( s );
}
    
std::vector<std::string> utils::split(
    const std::string &s, const char delimiter, const int flags )
{
    const bool trimmed( flags & SPLIT_TRIMMED  )
             , noEmpty( flags & SPLIT_NO_EMPTY )
    ;
    std::vector<std::string> tokens;
    std::stringstream ss( s );
    for( std::string t; getline( ss, t, delimiter ); )
    {
        if( trimmed ) trim( t );
        if( noEmpty && t.empty() ) continue;
        tokens.push_back( t );
    }
    return tokens;
}

使用示例:

const auto parts( utils::split( 
    " , a g , b, c, ", ',', utils::SPLIT_TRIMMED | utils::SPLIT_NO_EMPTY ) );