有效地获得字符串流的子流

时间:2011-08-16 07:57:07

标签: c++ parsing performance substring stringstream

我需要实现一种机制,我可以使用文本源初始化我的自定义类的向量,其中源的每一行代表我的类的一个实例。为实现这一目标,我为我的班级和operator >>实施了stringstream。当我读取源代码时,我逐行进行并获得原始源的子流,然后每次解析子流。这对我有三个好处。首先,通过这种方式,我可以确保文本源的一行代表我的类的一个实例。其次,由于忽略了解析后的其余行,我可以安全地在我的文本源的任何行中添加任何注释,这肯定会被解析器忽略。第三,我不需要提到原始源中向量的长度,因为我第一次得到解析错误(我检查流的failbad位以确认这个)我知道矢量声明结束了。

要逐行解析,我使用以下代码:

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

2 个答案:

答案 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>());