peek() Multiple Places Ahead?
Asked Answered
A

2

7

Lets say I have an outer while loop to read each character and output it to console. I also want to flag a word if it is found and by using the peek method I can find the first instance of a word. Is there a way to peek multiple places ahead. For example, if I looking for the word "payday" . I know I can input this into a string and search a string, but I want to read files in binary mode and I don't want to take away any values from the outer loop. If I have an inner loop with a read method, those values are not then displayed via the outer loop.

Thanks

int main()

ifstream strm;
char *chr = new char;

strm.open("mytext.txt",ios::out | ios::binary);

while (strm.read(chr,1)
{
    if (strm.peek() == 'p';
  {
    cout << "found a word beginning with 'p'" << endl;
  //what if I want to read multiple characters ahead.  Peek will read only one.
  }

}
Ardin answered 15/12, 2013 at 13:58 Comment(1)
Why are you using dynamic allocation?Dysfunction
A
10

You can use seekg method to jump around within the istream.

If you open your file in binary mode, you can use tellg method to mark the position to jump to.

However, if you open your file in text mode, it is better to jump with offset instead (i.e. strm.seekg(offset, strm.cur)), due to how multibyte characters like newline are counted by tellg and seekg in text mode. So if you detect that the next character is going to be "p", then you can read the next n character, then jump back -n character if it's not what you're looking for.

Aurora answered 15/12, 2013 at 14:9 Comment(3)
Thanks both. Either solution will work here but I've never really used seekg before and now that I've discovered it, it does exactly what I need. Thanks very much.Ardin
@domonica: note that having a proper parser is usually a better solution in the long run because proper parsing with proper error handlings, robustness against whitespace changes, and secure escaping means that the parsing job can grow in complexity very quickly. Even better is to use a well established format like xml or json or csv instead of defining your own format, and use a well debugged library to write and load them.Aurora
Problem is, this only works on seekable streams -- if the stream is reading from something non-seekable (such as a terminal or a network socket), seekg will fail.Hesperidium
A
5

There are multiple ways to achieve this, however the conventional one is simply to add one more layer between the original file and the "user" functions: a lexer.

For example, a Lexer with unlimited buffering:

class Lexer {
public:
    Lexer(std::istream& s): source(s) { this->read(); }

    explicit operator bool() const {
        return not queue.empty();
    }

    Lexer& operator>>(std::string& s) {
        assert(*this and "Test for readiness before calling this method");

        s = queue.front();
        queue.pop_front();

        if (queue.empty()) { this->read(); }
        return *this;
    }

    std::string const* peek(size_t const i) {
        while (source and queue.size() < i) { this->read(); }
        return queue.size() >= i ? &queue[i] : nullptr;
    }

private:
    void read() {
        queue.emplace_back();
        if (not (source >> queue.back())) { queue.pop_back(); }
    }

    std::istream& source;
    std::deque<std::string> queue;
}; // class Lexer

Note: obviously, you could perfectly limit the buffering of the lexer, or make it buffer something else than words, etc... the main advantage of a custom class is that you dictate semantics!

Abutilon answered 15/12, 2013 at 14:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.