昨天在网上看到,C++ 至今为止没有官方实现的字符串分割函数。相比 Python、Java 等语言,多少是有些不便的。
这里我们来在 C++ 中实现字符串分割函数。
利用来自 C 的 strtok
函数
C 语言的 string.h
中提供了名为 strtok
函数,用于对 C 风格的字符串进行分割。其函数签名为
1
| char* strtok(char* str, const char* delim);
|
当 str
不是空指针时,strtok
会从头开始寻找第一个合法的分隔符,而后将分隔符替换成 \0
,并将分隔符的位置保存在一个静态变量中,最后返回 str
。这样,按照 C 风格的字符串,我们就能获取分割得到的第一个 token。当 str
是空指针时,strtok
将会从记录的空指针处继续尝试分割。
因此,我们可以定义这样的 split
函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| #include <iostream> #include <cstring> #include <string> #include <vector> #include <algorithm>
void split(const std::string& s, std::vector<std::string>& sv, const char* delim = " ") { sv.clear(); char* buffer = new char[s.size() + 1]; buffer[s.size()] = '\0'; std::copy(s.begin(), s.end(), buffer); char* p = std::strtok(buffer, delim); do { sv.push_back(p); } while ((p = std::strtok(NULL, delim))); delete[] buffer; return; }
int main() { std::string s("abc:def::ghi"); std::vector<std::string> sv;
split(s, sv, ":");
for (std::vector<std::string>::const_iterator iter = sv.begin(); iter != sv.end(); ++iter) { std::cout << *iter << std::endl; }
return 0; }
|
对于 split
函数来说,它无法预知传入的 sv
变量的情况。因此,在 (1) 处,我们将 sv
这个 std::vector<std::string>
清空备用。由于 std::strtok
函数需要修改传入的 str
的内容,所以它需要 char*
类型的参数。故而,在 (2)(3) 两处,我们将 std::string
当中的内容复制一份。(4)(6) 两处对 std::strtok
的调用,帮助我们将 token 逐个压入 sv
当中。
在 C++11 及更高版本中,(5) 可替换为 sv.emplace_back(p)
,以避免额外的拷贝。
上述代码的结果是:
利用 C++ 中的流
实际上,利用纯 C++ 风格的代码,也是可以实现一个优雅的字符串分割函数的。
在前作中,我们介绍了 C++ 的 std::getline
函数。它接收一个输入流,将输入流至行末/分隔符部分的字符串保存在临时的字符串中;同时,返回输入流的左值引用。考虑到输入流本身可以用作条件判断,我们可以将 std::getline
与 while
循环联用,达成目的。
简单实现如下:
split.cc1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| #include <iostream> #include <sstream> #include <string> #include <vector>
void split(const std::string& s, std::vector<std::string>& sv, const char delim = ' ') { sv.clear(); std::istringstream iss(s); std::string temp;
while (std::getline(iss, temp, delim)) { sv.emplace_back(std::move(temp)); }
return; }
int main() { std::string s("abc:def:ghi"); std::vector<std::string> sv;
split(s, sv, ':');
for (const auto& s : sv) { std::cout << s << std::endl; }
return 0; }
|
代码中,我们借助字符串输入流 istringstream
处理带分割的字符串 s
。而后将各个 delim
之间的内容,保存在临时字符串 temp
当中,并移动到向量 sv
的末尾。
上述代码的结果是: