stringstream与getline
在刷题过程中遇到了根据特定字符划分字符串的场景,使用到了stringstream
和getline
,学习一下这种划分字符串的思路。
stringstream
stringstream
字符串流,在头文件<sstream>
中,本质上就是内存中的字符串流,可以像操作文件流fstream
一样处理字符串,也可以支持数据的<<
和>>
操作。
最主要的作用就是和getline
配合,将字符串转换为“流”,然后按照某种规则提取片段或者分割字符串。
getline
getline
是属于头文件<string>
中的一个函数,主要用于从输入流(cin
,stringstream
)中读取数据,直到遇到指定的分隔符为止,就是通过这一点特性可以用来做字符串分割
函数原型
istream& getline(istream& is, string& str, char delim);
其中:参数is
表示输入流,str
用来存储读取结果的字符串,delim
为自定义分隔符
简单来说就是:从is
中读取字符串str
,直到遇到了指定分隔符delim
,这个分隔符会被读取但不会被存入str
,并且会自动将这个字符从流中删除,或者直到末尾就结束。
使用
按照leetcode71
题为例,按照/
拆分字符串,使用stringstream
和getline
:
1、初始化stringstream
需要将目标字符串(待分割的字符串)传入流,让它变成可以读取的流,例如题目中的/home//foo/
string path = "/home//foo/";
stringstring ss(path); /// 转换成字符串流
2、使用getline
提取片段
分割条件是/
,循环从ss
中提取字串,每次得到的内容就是两个/
之间的内容,题目中说多个/
只保留一个,所以多个/
之间提取到的字串就是空,如果为空,跳过就行
则path = "/home//foo/"
的拆分过程如下:
while (getline(ss, s, '/')){
/// 处理逻辑
}
- 第一次调用
getline(ss, part, '/')
: 从流的起始位置开始,遇到第一个'/'
停止。此时part
为空字符串(因为'/'
是第一个字符,前面没有内容)。 - 第二次调用
getline(ss, part, '/')
: 从第一个'/'
之后开始读取,直到第二个'/'
停止。读取到的内容是"home"
,存入part
。 - 第三次调用
getline(ss, part, '/')
: 从第二个'/'
之后开始读取,遇到第三个'/'
停止。由于两个'/'
之间没有字符,part
为空字符串。 - 第四次调用
getline(ss, part, '/')
: 从第三个'/'
之后开始读取,遇到第四个'/'
停止。读取到的内容是"foo"
,存入part
。 - 第五次调用
getline(ss, part, '/')
: 从第四个'/'
之后开始读取,到达流的末尾(没有更多字符)。part
为空字符串。
3、处理一些其他的附加条件,比如空字符串(由连续'/'
产生)和"."
(当前目录)会被忽略,或者..
会触发回退到上一级的操作(这里只需要将栈顶元素弹出就行)
主要特点
1、stringstream
和getline
这个组合可以处理连续的分隔符,然后我们对拿到的字符串进行处理,如果是连续的分隔符则会读取到空字符字串。
2、这个组合保留了分割符之间的所有字符,>>
运算会跳过空白字符,getline
则会完整保留分隔符之间的所有字符,包括空格
3、可以自定义指定分割符(如'/'
、','
、';'
等),默认是\n
.
处理代码
所以这里处理字符串分割的代码示例如下:
vector<string> st; /// 用数组模拟栈
stringstream ss(path);
string s;
// 循环读取
while (getline(ss, s, '/')) {
if (s.empty() || s == ".") continue;
if (s != "..") st.push_back(s);
else if (!st.empty()) st.pop_back();
}
// 现在st就是存放正确路径的字符串部分,再使用一个string把这些穿起来就行