istringstream in c++

问题起源

在刷leetcode的时候,看到了discuss里面有一位大佬提出很有意思的解法

这是一道mini parser的题,题目描述如下:

Given a nested list of integers represented as a string, implement a parser to deserialize it.

Each element is either an integer, or a list -- whose elements may also be integers or other lists.

Note: You may assume that the string is well-formed:

  • String is non-empty.
  • String does not contain white spaces.
  • String contains only digits 0-9, [, - ,, `]

他的解法是:

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/**
* // This is the interface that allows for creating nested lists.
* // You should not implement it, or speculate about its implementation
* class NestedInteger {
* public:
* // Constructor initializes an empty nested list.
* NestedInteger();
*
* // Constructor initializes a single integer.
* NestedInteger(int value);
*
* // Return true if this NestedInteger holds a single integer, rather than a nested list.
* bool isInteger() const;
*
* // Return the single integer that this NestedInteger holds, if it holds a single integer
* // The result is undefined if this NestedInteger holds a nested list
* int getInteger() const;
*
* // Set this NestedInteger to hold a single integer.
* void setInteger(int value);
*
* // Set this NestedInteger to hold a nested list and adds a nested integer to it.
* void add(const NestedInteger &ni);
*
* // Return the nested list that this NestedInteger holds, if it holds a nested list
* // The result is undefined if this NestedInteger holds a single integer
* const vector<NestedInteger> &getList() const;
* };
*/
class Solution {
public:
NestedInteger deserialize(string s) {
istringstream in(s);
return deserialize1(in);
}
private:
NestedInteger deserialize1(istringstream& in){
int num;
if(in >> num) return NestedInteger(num);
in.clear();
in.get();
NestedInteger lst;
while (in.peek()!=']'){
lst.add(deserialize1(in));
if(in.peek()==',') in.get();
}
in.get();
return lst;
}
};


关于istringstream

但是我今天要说的不是leetcode的解法,而是要介绍一种内存I/O——istringstream

这个类的对象可以直接从string中读取数据,最基本的

istringstream是基于空格来提取字符串的,从而完成从字符串到其它类型的改变


基于空格提取字符串

1
2
3
4
5
6
7
8
9
10
11
12
#include <sstream>
#include <string>
using std::string;
using std::istringstream;
int main()
{
string a("123a 456 [789]]]");
istringstream in(a);
string s1, s2, s3;
in >> s1 >> s2 >> s3; //s1:"123a", s2:"456", s3:"[789]]]";
return 0;
}


从字符串转化成数字(int / double)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <sstream>
#include <string>
#include <iostream>
using std::string;
using std::istringstream;
using std::cout;
using std::endl;
int main()
{
string a("123a ,[456,[789]]]");
istringstream in(a);
int i1, i2;
string s1, s2;
in >> i1;
//in.clear();
in >> s1 >> s2;
cout << i1 << endl;
cout << s1 << endl;
cout << s2 << endl;
return 0;
}
  • 这里的i1为123,s1为“a”,s2为“,[456,[789]]]”;
  • in能顺利将流输入到int类型的i1中,当遇到a时,流输入暂停;开始下一次的输入;


低层的I/O操作

首先引入几个流状态:

iostate value(member constant) indicates functions to check state flags
good() eof() fail() bad() rdstate()
goodbit No errors (zero value iostate) true false false false goodbit
eofbit End-of-File reached on input operation false true false false eofbit
failbit Logical error on i/o operation false false true false failbit
badbit Read/writing error on i/o operation false false true true badbit

引用自:http://www.cplusplus.com/reference/ios/ios/clear/

因此我们来修改一下啊刚才的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <sstream>
#include <string>
#include <iostream>
using std::string;
using std::istringstream;
using std::cout;
using std::endl;
int main()
{
string a("a123a ,[456,[789]]]");
istringstream in(a);
int i1, i2;
string s1;
in >> i1;
//in.clear();
in >> s1;
cout << i1 << endl;
cout << s1 << endl;
return 0;
}
  • 此时,s1,i1都没有被输入正确的数,由于刚开始遇到了'a',流状态被设置为failbit;此时认为流已经结束;

为了避免这种情况发生,我们使用类内成员clear()去清除该状态,使得流状态处于goodbit;把注释去掉,我们可以看到s1变成了"a123a",也就是从错误的地方开始;


此时已经解决了部分的问题,但是如果我们仍然想要123这个整数怎么办?

此时引入了另外几个成员函数:get()peek()

  • is.get():将is的下一个字节作为int返回,并从流中删除它;
  • is.peek():将is的下一个字节作为int返回,但不从流中删除它;

观察刚刚的例子,我们可以看到第一个字符为'a',因此我们可以从流中获取并删除它:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <string>
#include <iostream>
using std::string;
using std::istringstream;
using std::cout;
using std::endl;
int main()
{
string a("a123a ,[456,[789]]]");
istringstream in(a);
int i1, i2;
string s1;
in >> i1;
in.clear();
auto m = in.get();
cout << m << endl;
in >> i1;
in >> s1;
cout << i1 << endl;
cout << s1 << endl;
return 0;
}
  • 此时,输出m为字符'a'的ascii码97,i1为123,s1为“a";