Как использовать QFile с std :: iostream? - PullRequest
9 голосов
/ 05 марта 2011

Можно ли использовать QFile как std :: iostream? Я совершенно уверен, что там должна быть обертка. Вопрос где?

У меня есть другие библиотеки, для которых в качестве входного параметра требуется std :: istream, но в моей программе у меня есть только QFile.

Ответы [ 5 ]

7 голосов
/ 05 марта 2011

Я придумал собственное решение, используя следующий код:

#include <ios>
#include <QIODevice>

class QStdStreamBuf : public std::streambuf
{
public:
    QStdStreamBuf(QIODevice *dev) : std::streambuf(), m_dev(dev)
    {
        // Initialize get pointer.  This should be zero so that underflow is called upon first read.
        this->setg(0, 0, 0);
    }

protected:
virtual std::streamsize xsgetn(std::streambuf::char_type *str, std::streamsize n)
{
    return m_dev->read(str, n);
}

virtual std::streamsize xsputn(const std::streambuf::char_type *str, std::streamsize n)
{
    return m_dev->write(str, n);
}

virtual std::streambuf::pos_type seekoff(std::streambuf::off_type off, std::ios_base::seekdir dir, std::ios_base::openmode /*__mode*/)
{
    switch(dir)
    {
        case std::ios_base::beg:
            break;
        case std::ios_base::end:
            off = m_dev->size() - off;
            break;
        case std::ios_base::cur:
            off = m_dev->pos() + off;
            break;
    }
    if(m_dev->seek(off))
        return m_dev->pos();
    else
        return std::streambuf::pos_type(std::streambuf::off_type(-1));
}
virtual std::streambuf::pos_type seekpos(std::streambuf::pos_type off, std::ios_base::openmode /*__mode*/)
{
    if(m_dev->seek(off))
        return m_dev->pos();
    else
        return std::streambuf::pos_type(std::streambuf::off_type(-1));
}

virtual std::streambuf::int_type underflow()
{ 
    // Read enough bytes to fill the buffer.
    std::streamsize len = sgetn(m_inbuf, sizeof(m_inbuf)/sizeof(m_inbuf[0]));

    // Since the input buffer content is now valid (or is new)
    // the get pointer should be initialized (or reset).
    setg(m_inbuf, m_inbuf, m_inbuf + len);

    // If nothing was read, then the end is here.
    if(len == 0)
        return traits_type::eof();

    // Return the first character.
    return traits_type::not_eof(m_inbuf[0]);
}


private:
    static const std::streamsize BUFFER_SIZE = 1024;
    std::streambuf::char_type m_inbuf[BUFFER_SIZE];
    QIODevice *m_dev;
};

class QStdIStream : public std::istream
{
public:
    QStdIStream(QIODevice *dev) : std::istream(m_buf = new QStdStreamBuf(dev)) {}
    virtual ~QStdIStream()
    {
        rdbuf(0);
        delete m_buf;
    }

private:
    QStdStreamBuf * m_buf;
};

У меня нормально работает чтение локальных файлов. Я не проверял это для записи файлов. Этот код, конечно, не идеален, но он работает.

5 голосов
/ 05 марта 2011

Я придумал собственное решение (в котором используется та же идея, что предложил Стивен Чу)

#include <iostream>
#include <fstream>
#include <cstdio>

#include <QtCore>

using namespace std;

void externalLibFunction(istream & input_stream) {
    copy(istream_iterator<string>(input_stream),
         istream_iterator<string>(),
         ostream_iterator<string>(cout, " "));
}

ifstream QFileToifstream(QFile & file) {
    Q_ASSERT(file.isReadable());        
    return ifstream(::_fdopen(file.handle(), "r"));
}

int main(int argc, char ** argv)
{
    QFile file("a file");
    file.open(QIODevice::WriteOnly);
    file.write(QString("some string").toLatin1());
    file.close();
    file.open(QIODevice::ReadOnly);
    std::ifstream ifs(QFileToifstream(file));
    externalLibFunction(ifs);
}

Выход:

some string

В этом коде используется конструктор перемещения std :: ifstream (функция C ++ x0), заданный в 27.9.1.7 конструкторах basic_ifstream раздел Рабочий проект, стандартный для языка программирования C ++ :

basic_ifstream(basic_ifstream&& rhs);
Эффекты: перемещение конструкций из Значение RHS. Это достигается перейти к созданию базового класса, и содержащийся basic_filebuf. следующий basic_istream :: set_rdbuf (& sb) вызывается для установки содержимого basic_filebuf. * ​​1018 *

См. Как вернуть fstream (C ++ 0x) для обсуждения этой темы.

4 голосов
/ 05 марта 2011

Если полученный вами объект QFile еще не открыт для чтения, вы можете получить из него имя файла и открыть объект ifstream.

Если он уже открыт, вы можете получить дескриптор / дескриптор файла с помощью handle() и иди оттуда.Нет никакого портативного способа получить поток от ручки платформы.Вам придется найти обходной путь для ваших платформ.

1 голос
/ 06 июля 2018

Вот хорошее руководство по созданию подкласса std :: streambuf для предоставления недоступного для чтения std :: istream: https://stackoverflow.com/a/14086442/316578

Вот простой класс, основанный на этом подходе, который адаптирует QFile в std :: streambuf, который затем может быть заключен в std :: istream.

#include <iostream>
#include <QFile>

constexpr qint64 ERROR = -1;
constexpr qint64 BUFFER_SIZE = 1024;

class QFileInputStreamBuffer final : public std::streambuf {
private:
    QFile &m_file;
    QByteArray m_buffer;
public:
    explicit QFileInputStreamBuffer(QFile &file)
        : m_file(file),
          m_buffer(BUFFER_SIZE, Qt::Uninitialized) {
    }

    virtual int underflow() override {
        if (atEndOfBuffer()) {
            // try to get more data
            const qint64 bytesReadIntoBuffer = m_file.read(m_buffer.data(), BUFFER_SIZE);
            if (bytesReadIntoBuffer != ERROR) {
                setg(m_buffer.data(), m_buffer.data(), m_buffer.data() + bytesReadIntoBuffer);
            }
        }
        if (atEndOfBuffer()) {
            // no more data available
            return std::char_traits<char>::eof();
        }
        else {
            return std::char_traits<char>::to_int_type(*gptr());
        }
    }

private:
    bool atEndOfBuffer() const {
        return gptr() == egptr();
    }
};

Если вы хотите иметь больше возможностей, таких как поиск, запись и т. Д., То вам понадобится одно из других более сложных решений, которое переопределяет больше функций streambuf. * ​​1007 *

0 голосов
/ 05 марта 2011

Если вам не важна производительность, вы всегда можете прочитать все из файла и записать его в std::stringstream, а затем передать его в свою библиотеку. (или в противном случае, буферизируйте все в поток строк, а затем записывайте в файл QFile)

Кроме этого, не похоже, что они могут взаимодействовать. Во всяком случае, операции с Qt по STL часто являются причиной неясных ошибок и тонких несоответствий, если версия STL, с которой была скомпилирована Qt, каким-либо образом отличается от используемой вами версии STL. Это может произойти, например, если вы измените версию Visual Studio.

...