Search code examples
c++qtstdistreamqbytearray

How to convert QByteArray to std::istream or std::ifstream?


I want to create istream from QByteArray at runtime, without saving a physical file in memory of QByteArray.

I found that there are many ways to do the opposite conversion, i.e. istream to QByteArray, but not this one.

How to accomplish that?


Solution

  • To read via std::istringstream from QByteArray seems quite easy:

    testQByteArray-istream.cc:

    #include <iostream>
    #include <sstream>
    #include <QtCore>
    
    int main()
    {
      qDebug() << "Qt Version:" << QT_VERSION_STR;
      // make a QByteArray
      QByteArray data("Hello Qt World.");
      // convert to std::string
      std::istringstream in(data.toStdString());
      // read from istringstream
      for (;;) {
        std::string buffer;
        if (!std::getline(in, buffer)) break;
        std::cout << "Got: '" << buffer << "'\n";
      }
      // done
      return 0;
    }
    

    testQByteArray-istream.pro:

    SOURCES = testQByteArray-istream.cc
    
    QT = core
    

    Compiled and tested on cygwin64:

    $ qmake-qt5 testQByteArray-istream.pro
    
    $ make
    
    $ ./testQByteArray-istream 
    Qt Version: 5.9.4
    Got: 'Hello Qt World.'
    
    $
    

    Done. Stop, wait!

    without saving a physical file in memory

    I'm not quite sure how to read this. Probably, it means

    without copying data saved in QByteArray

    I see only two solutions:

    1. Use a QDataStream instead of std::stream. According to doc. QDataStream::QDataStream(const QByteArray &a)

      Constructs a read-only data stream that operates on byte array a.

      This sounds very promising that data is not copied.

    2. DIY. Make a class derived from std::stream which may read from a QByteArray without copying.

    Concerning 2. option, I found Dietmar Kühl's answer to SO: Creating an input stream from constant memory. Applying this to the above sample, it would look like this:

    #include <iostream>
    #include <QtCore>
    
    // borrowed from https://stackoverflow.com/a/13059195/7478597
    struct membuf: std::streambuf {
      membuf(char const* base, size_t size) {
        char* p(const_cast<char*>(base));
        this->setg(p, p, p + size);
      }
    };
    struct imemstream: virtual membuf, std::istream {
      imemstream(char const *base, size_t size):
        membuf(base, size),
        std::istream(static_cast<std::streambuf*>(this)) {
      }
    };
    
    int main()
    {
      qDebug() << "Qt Version:" << QT_VERSION_STR;
      // make a QByteArray
      QByteArray data("Hello Qt World.");  
      imemstream in(data.data(), (size_t)data.size());
      // read from istringstream
      for (;;) {
        std::string buffer;
        if (!std::getline(in, buffer)) break;
        std::cout << "Got: '" << buffer << "'\n";
      }
      // done
      return 0;
    }
    

    Compiled and tested again on cygwin64:

    $ qmake-qt5 testQByteArray-istream.pro
    
    $ make
    
    $ ./testQByteArray-istream 
    Qt Version: 5.9.4
    Got: 'Hello Qt World.'
    
    $