lovingxiaobing 发表于 2019-12-8 02:10

【C++】 字符串分割 (split)

昨天,有人问我,C++标准库有没有字符串分割的函数。
emmmm,我特意去翻了翻,好像没有。

于是我们可以可以简单封装一个。

在设计这个字符串分割函数以前,我们思考一下这个函数都有哪些规则。

我们给这个函数起名为"split"
这个函数有两个参数,用来接收待分割的字符串(str)和分隔符(delim),这个分隔符是一个字符串。
这个函数将分割得到的子串存入一个容器中,这里可以选择 std:vector。

定义几个规则:

[*]不对空字符串进行分割
[*]空分隔符不存在,即不对字符串进行分割,原封不动的加入到容器中
[*]根据分隔符对字符串进行分割
[*]字符串中没有找到分隔符不添加到容器


为了最小依赖,我们可以完全使用C++标准库中提供的方法。我们会用到两个C++标准库字符串类提供的方法。

[*]std::basic_string<CharType>::find:搜索子串在字符串中出现的位置
[*]std::basic_string<CharType>::substr :返回某个范围内的子串

(std::string是std::basic_string<char>的类型别名)

如果没有find到子串,会返回std::basic_string<CharType>::npos;

这个字符串分割就是在字符串中搜索子串分隔符出现的位置,然后提取出当前上一个位置+分隔符长度到下一个分隔符的位置之间的子串。

算法很简单吧!

为了同时兼容char和wchar_t,我对split进行了泛化。可以同时适应char和wchar_t以及其他宽度的字符序列,但都是以std::basic_string为模板。

下面就是具体代码,主要函数就split,其他的都是用来测试split函数的。


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

inline namespace {
////////
// split: 字符串分割
// str : 被分割的字符
// delim : 分隔符
// 返回一个std::vector容器里面容纳被分割后的字符串
template <typename CharT>
auto split(const std::basic_string<CharT>& str,
             const std::basic_string<CharT>& delim)
               -> std::vector<std::basic_string<CharT>> {
    std::vector<std::basic_string<CharT>> strVec;
   
    // 空字符串不分割
    if (!str.length())
      return strVec;
   
    // 空分隔符不分割
    if (!delim.length()) {
      strVec.push_back(str);
      return strVec;
    }
   
    typename std::basic_string<CharT>::size_type start{};
    typename std::basic_string<CharT>::size_type end{};
   
    // 找不到分隔符不添加
    end = str.find(delim, start);
    if (end == std::basic_string<CharT>::npos) {
      return strVec;
    } else {
      strVec.push_back(std::move(str.substr(start, (end-start))));
      start = end + delim.length();
    }
   
    // 不断分割添加子串到容器
    while (end != std::basic_string<CharT>::npos) {
      end = str.find(delim, start);
      strVec.push_back(std::move(str.substr(start, (end-start))));
      start = end + delim.length();
    }
   
    return strVec;
}



////////////////////////////////////////
// 测试用例


void Print(const std::string& str, std::string end = "") {
    std::cout << str << end;
}
void Print(const std::wstring& wstr, std::wstring end = L"") {
    std::wcout << wstr << end;
}

template <typename T>
void PrintVector(const std::vector<T>& vec) {
    size_t idx{};
    Print(L"[");
    for (const auto& e : vec) {
      Print(L"\"");
      Print(e);
      Print(L"\"");
      if (++idx < vec.size()) {
      Print(L", ");
      }
    }
    Print(L"]", L"\n");
}

void DemoMultibytes() {
    Print("多字节测试:", "\n");
    std::string str{"&&Hello&,&&I'm 小冰哟&&&#128514;&xx"};
    std::string delim{"&&"};
   
    Print("字符串: \"", str + "\"");
    Print("\n分隔符: \"", delim + "\"");
    Print("\n");
   
    /////////////////////////////////////
    // vec = split(...)
    std::vector<std::string> vec{split(str, delim)};
    /////////////////////////////////////
   
    PrintVector(vec);
}

void DemoWideChar() {
    // ...
    {
      std::ios::sync_with_stdio(false);
      std::wcout.imbue(std::locale(""));
    }
   
    Print(L"宽字符测试:", L"\n");
   
    std::wstring wstr{L"&&Hello&,&&I'm 小冰哟&&&#128514;&xx"};
    std::wstring wdelim{L"&&"};
   
    Print(L"宽字符串: \"", wstr + L"\"");
    Print(L"\n分隔符: \"", wdelim + L"\"");
    Print(L"\n");
   
    //////////////////////////////////////
    // wvec = split(...)
    std::vector<std::wstring> wvec{split(wstr, wdelim)};
    //////////////////////////////////////
   
    PrintVector(wvec);
}


void test() {
    DemoMultibytes();
    Print("***************", "\n");
    DemoWideChar();
}
}

int main() {
test();
return 0;
}


我的C++代码中用了一些C++新标准的东西,保证自己的编译器支持C++11/14以上。

运行结果:



下面是相关资料的链接

https://en.cppreference.com/w/cpp/string/basic_string
https://en.cppreference.com/w/cpp/string/basic_string/find
https://en.cppreference.com/w/cpp/string/basic_string/substr

silencereader 发表于 2019-12-18 10:10

刚好上次遇到了这个问题,把我折磨了一个星期,现在学习一下
页: [1]
查看完整版本: 【C++】 字符串分割 (split)