C++ 分割字符串方法汇总
引言
557. 反转字符串中的单词 III - 力扣(LeetCode)
本文是我在遇到这个题目时脑子里第一时间蹦出如何能够像Python那样优雅的分割而产生的。
做完看题解后才发现我把简单问题想复杂了……但也不失为一种解决方案,于是再此记录一下。
方法一:使用C风格字符串函数
C语言中提供了strtok
和strtok_r
函数用于字符串分割。strtok
函数用于分割字符串,但存在线程安全问题,而strtok_r
是线程安全的版本。
使用示例(strtok_r
为例)
strtok_r
函数原型:char *strtok_r(char *str, const char *del, char **saveptr);
str
:指向待分割的指针,第一次调用strtok_r
时,应该传入待分割字符串,在后续调用中,如果继续分割同一个字符串,str
应该设置为NULL
。del
:包含一个或多个分隔符的字符串。strtok_r
将使用这个字符串中的任何一个字符作为分隔符来分割原始字符串。saveptr
:指向char *
类型的指针的指针。strtok_r
函数使用save_ptr
来保存它内部的状态信息,这样在后续的调用中可以继续从上次停止的地方开始分割。在第一次调用strtok_r
时,save_ptr
的值可以是任意值,因为它会被strtok_r
初始化。在后续调用中,save_ptr
应该保持不变,以便strtok_r
可以正确地恢复分割的位置。
#include <cstring> // 包含必要的头文件
#include <iostream>
#include <vector>
using namespace std;
// 定义函数用于使用C风格函数分割字符串
vector<string> split_c_style(const char* str, char delimiter) {
vector<string> result; // 创建结果向量
char* token; // 用于保存分割得到的子串
char* rest = strdup(str); // 使用strdup复制字符串,避免修改原字符串
// 使用strtok_r分割字符串,保存中间状态
token = strtok_r(rest, &delimiter, &rest);
while (token != nullptr) {
result.push_back(token); // 将分割得到的子串添加到结果向量
token = strtok_r(nullptr, &delimiter, &rest); // 继续分割剩余的字符串
}
// 释放strdup分配的内存
free(rest);
return result;
}
注意事项:
strtok
和strtok_r
会修改原始字符串,因此通常需要先复制字符串。- 使用
strtok_r
时,需要管理额外的char*
指针来保存中间状态。
方法二:使用istringstream
istringstream
是C++标准库中的流类,可以用于解析字符串,类似于从文件中读取数据。
使用示例
#include <sstream> // 包含必要的头文件
#include <vector>
#include <string>
using namespace std;
// 定义函数使用istringstream分割字符串
vector<string> split_with_istringstream(const string& str, char delimiter) {
vector<string> result; // 创建结果向量
istringstream iss(str); // 创建istringstream对象
string token;
// 使用getline读取子串,直到遇到分隔符
while (getline(iss, token, delimiter)) {
result.push_back(token); // 将子串添加到结果向量
}
return result;
}
优点:
- 更加面向对象,代码结构清晰。
- 不会修改原始字符串。
方法三:使用regex
regex
库提供了强大的正则表达式支持,可以用于复杂的字符串分割。
使用示例
#include <regex> // 包含必要的头文件
#include <vector>
#include <string>
using namespace std;
// 定义函数使用regex分割字符串
vector<string> split_with_regex(const string& str, const string& pattern) {
vector<string> result; // 创建结果向量
regex re(pattern); // 创建regex对象
sregex_token_iterator it(str.begin(), str.end(), re, -1); // 创建迭代器
sregex_token_iterator reg_end; // 结束迭代器
// 遍历迭代器,将子串添加到结果向量
for (; it != reg_end; ++it) {
result.push_back(*it);
}
return result;
}
优点:
- 可以处理更复杂的分割模式,如忽略连续的分隔符。
- 提供了强大的正则表达式功能。
方法四:手动实现分割
对于简单的需求,手动实现字符串分割函数也是一个不错的选择,可以完全控制分割逻辑。
使用示例
#include <vector>
#include <string>
using namespace std;
// 定义函数手动分割字符串
vector<string> manual_split(const string& str, char delimiter) {
vector<string> result; // 创建结果向量
size_t start = 0; // 开始位置
size_t end = str.find(delimiter); // 结束位置
// 循环查找分隔符,直到遍历完整个字符串
while (end != string::npos) {
result.push_back(str.substr(start, end - start)); // 将子串添加到结果向量
start = end + 1; // 更新开始位置
end = str.find(delimiter, start); // 查找下一个分隔符的位置
}
// 添加最后一个子串到结果向量
result.push_back(str.substr(start));
return result;
}
优点:
- 完全控制逻辑,适用于特定需求。
- 不依赖额外的库函数。
总结
以上四种方法各有优缺点,选择哪一种取决于具体的应用场景和需求。对于简单的字符串分割,使用istringstream
或手动实现分割函数可能更为合适。对于复杂的分割需求,regex
提供了强大的功能。而在处理C风格字符串或需要线程安全的场景下,strtok_r
可以作为一个备选方案。