我需要实现一种机制,我可以使用文本源初始化我的自定义类的向量,其中源的每一行代表我的类的一个实例。为实现这一目标,我为我的班级和operator >>
实施了stringstream
。当我读取源代码时,我逐行进行并获得原始源的子流,然后每次解析子流。这对我有三个好处。首先,通过这种方式,我可以确保文本源的一行代表我的类的一个实例。其次,由于忽略了解析后的其余行,我可以安全地在我的文本源的任何行中添加任何注释,这肯定会被解析器忽略。第三,我不需要提到原始源中向量的长度,因为我第一次得到解析错误(我检查流的fail
和bad
位以确认这个)我知道矢量声明结束了。
要逐行解析,我使用以下代码:
std::stringstream fullStream;
std::stringstream lineStream;
std::string str;
bool isValid;
myClass newInstance;
std::vector < myClass > result;
// Fill fullStream from external source (codepart omitted)
isValid = true;
while ( isValid && ! fullStream.eof ( ) ) {
std::getline ( fullStream, str );
lineStream.clear ( );
lineStream.str ( str );
lineStream >> newInstance;
isValid = ! lineStream.fail ( );
if ( isValid ) {
result.push_back ( newInstance );
}
}
虽然这段代码运行正常,但我想知道是否有更好的方法来实现相同的结果。特别是,如果有更有效的方法从fullStream
提取行到lineStream
。
谢谢,
ADAM
答案 0 :(得分:3)
首先,如果代码有效,那实际上只是偶然。惯用的 处理这个问题的方法是:
std::string line;
while ( std::getline( fullStream, line ) ) {
std::istringstream lineStream( line );
lineStream >> newInstance;
if ( lineStream ) {
result.push_back( newInstance );
} else {
fullStream.setstate( std::ios_base::failbit );
}
}
在读取之前检查eof()
很少有用,而不是检查
使用前getline
的结果几乎肯定是错误的。
尝试重用stringstream
比使用fullStream
更复杂,更容易出错
简单地创建一个新的;有各种各样的州可能或可能
不必重置。 Streams有一种记忆错误的机制
状态,所以你可能想要使用它。 (如果你想继续使用
std::istringstream
对于错误后的其他事情,问题更多
复杂,因为你已经提取了失败的那条线,而你
不能把它放回去。)如果你只是阅读,你应该使用
std::stringstream
,而非{{1}}(其中有很多
额外行李);一般来说,使用双向非常非常罕见
流。
答案 1 :(得分:2)
一个明显的替代方案是让你的operator>>
逐行阅读,这样你就不必在外部做到这一点:
class MyClass {
// some sort of data to demonstrate the idea:
int x;
std::string y;
friend std::istream &operator>>(std::istream &is, MyClass &m) {
std::string temp;
std::getline(is, temp);
std::istringstream buffer(temp);
buffer >> m.x >> m.y;
return is;
}
};
有了这个,从文件中读取数据的代码变得更加简单:
std::copy(std::istream_iterator<MyClass>(fullStream),
std::istream_iterator<MyClass>(),
std::back_inserter(result));
编辑:如果您不想直接在operator>>
MyClass
中构建面向行的阅读,则另一种可能性是使用代理类:
class LineReader {
MyClass object;
public:
operator MyClass() { return object; }
friend std::istream &operator>>(std::istream &is, LineReader &d) {
std::string line;
std::getline(is, line);
std::istringstream buffer(line);
buffer >> d; // delegate to the object's own stream-oriented reader.
}
};
然后,当您想要进行面向行的阅读时,您读取代理类的对象,但存储原始类的对象:
std::vector<MyClass>((std::istream_iterator<LineReader>(some_stream)),
std::istream_iterator<LineReader>());
但是,当/如果您想要读取对象流而不是对象行时,您可以直接使用对象自己的operator>>
:
std::vector<MyClass>((std::istream_iterator<MyClass>(stream),
std::istream_iterator<MyClass>());