有效获取字符串流的子流

发布于 2024-11-29 12:25:09 字数 1096 浏览 6 评论 0原文

我需要实现一种机制,可以使用文本源初始化自定义类的向量,其中源的每一行代表我的类的一个实例。为了实现这一目标,我为我的类和 stringstream 实现了 operator >>。当我阅读源代码时,我会逐行获取原始源代码的子流,然后每次解析该子流。这对我来说有三个好处。首先,这样我可以确保文本源的一行恰好代表我的类的一个实例。其次,由于解析后的行的其余部分被忽略,我可以安全地在文本源的任何行中添加任何注释,这肯定会被解析器忽略。第三,我不需要提及原始源中向量的长度,因为我第一次遇到解析错误(我检查了 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

谢谢,
阿达姆

I need to implement a mechanism where I can initialize a vector of my custom class using a text source, where each line of the source is representing one instance of my class. To achieve this, I implemented the operator >> for my class and stringstream. When I read the source, I go line-by-line and get a substream of my original source, then parse the substream each time. This has three benefits for me. First, this way I can make sure that one line of the text source would represent exactly one instance of my class. Second, as the rest of the line after parsing is ignored, I can safely add any comment in any line of my text source, which would surely get ignored by the parser. And third, I don't need to mention the length of the vector in my original source, since the first time I get a parsing error (I check the fail and bad bits of the stream to confirm this) I know that the vector declaration is over.

To parse line-by-line, I'm using the following code:

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 );
   }
}

Although this code works fine, I'm wondering if there was a better way to achieve the same result. Specially, if there was a more efficient way to extract a line from fullStream to lineStream.

Thanks,
Ádám

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

素食主义者 2024-12-06 12:25:09

首先,如果代码有效,那实际上只是偶然。惯用语
处理这个问题的方法是:

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 进行其他事情后出错,问题就多了
复杂,因为你已经提取了失败的行,并且你
不能把它放回去。)如果你只是在阅读,你应该使用
std::istringstream,而不是 std::stringstream (它有很多
额外行李);一般来说,使用双向的情况非常非常罕见
溪流。

First, if the code works, it is really only by chance. The idiomatic
way of handling this is:

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 );
    }
}

Checking eof() before a read is rarely useful, and not checking the
results of your getline before using it is almost certainly an error.
Trying to reuse a stringstream is more complex and error prone than
simply creating a new one; there is all sorts of state which may or may
not have to be reset. Streams have a mechanism for memorizing error
state, so you probably want to use this. (If you want to continue using
fullStream for other things after the error, the problem is more
complex, because you've already extracted the line which failed, and you
can't put it back.) And if you're only reading, you should use
std::istringstream, and not std::stringstream (which has a lot of
extra baggage); in general, it's very, very rare to use a bi-directional
stream.

橘味果▽酱 2024-12-06 12:25:09

一个明显的替代方案是让您的 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));

编辑:如果您不想将面向行的读取直接构建到 MyClassoperator>> 中,另一种可能性是使用代理类:

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

One obvious alternative would be to have your operator>> do line-by-line reading itself, so you don't have to do that externally:

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;
    }
};

With that, code to read data from a file becomes a little more straightforward:

std::copy(std::istream_iterator<MyClass>(fullStream),
          std::istream_iterator<MyClass>(),
          std::back_inserter(result));

Edit: if you don't want to build the line-oriented reading directly into the operator>> for MyClass, another possibility is to use a proxy class:

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.
    }
};

Then when you want to do line-oriented reading, you read objects of the proxy class, but store objects of the original class:

std::vector<MyClass>((std::istream_iterator<LineReader>(some_stream)), 
                      std::istream_iterator<LineReader>());

But, when/if you want to read a stream of objects instead of lines of objects, you use the object's own operator>> directly:

std::vector<MyClass>((std::istream_iterator<MyClass>(stream),
                      std::istream_iterator<MyClass>());
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文