昨天,有人问我,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函数的。
[C++] 纯文本查看 复制代码
#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 小冰哟&&😂&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 小冰哟&&😂&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
|