【问题标题】:How to use a QFile with std::iostream?如何将 QFile 与 std::iostream 一起使用?
【发布时间】:2011-07-09 10:15:16
【问题描述】:

是否可以使用像 std::iostream 这样的 QFile?我很确定那里一定有一个包装。问题是在哪里?

我有另一个库,它需要一个 std::istream 作为输入参数,但在我的程序中,此时我只有一个 QFile。

【问题讨论】:

  • That 看起来像一个解决方案,但它需要 Boost。
  • 您能接受非便携式解决方案吗?如果可以,您需要在哪个平台上工作?
  • boost::iostream 可以与 std::basic_stream 互换吗?

标签: qt stl iostream


【解决方案1】:

我使用以下代码提出了自己的解决方案:

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

我可以很好地读取本地文件。我还没有测试它来写文件。这段代码肯定不完美,但它确实有效。

【讨论】:

    【解决方案2】:

    我想出了自己的解决方案(使用了 Stephen Chu 建议的相同想法)

    #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
    

    此代码使用Working Draft, Standard for Programming Language C++27.9.1.7 basic_ifstream 构造函数 部分中指定的std::ifstream 移动构造函数(C++x0 特性):

    basic_ifstream(basic_ifstream&amp;&amp; rhs);
    效果:从 右值 rhs。这是通过 移动构建基类,以及 包含的 basic_filebuf。下一个 调用 basic_istream::set_rdbuf(&sb) 来安装包含的 basic_filebuf。

    有关此主题的讨论,请参阅 How to return an fstream (C++0x)

    【讨论】:

    • 对于ifstream(::_fdopen(file.handle(), "r")); 这行我得到:error: no matching function for call to 'std::basic_ifstream&lt;char&gt;::basic_ifstream(FILE*)' std::ifstream fileInputStream(::_fdopen(testPatternPng.handle(), "r"));
    【解决方案3】:

    如果您获得的 QFile 对象尚未打开以供读取,您可以从中获取文件名并打开一个 ifstream 对象。

    如果它已经打开,您可以使用handle() 获取文件句柄/描述符并从那里开始。没有从平台句柄获取 fstream 的便携式方法。您必须为您的平台找到一种解决方法。

    【讨论】:

    • 仅获取文件名不适用于 Qt 资源。如果 QFile 指向资源,handle() 方法会起作用吗?
    • 实际上,您可以使用 Boost.IOStreams 以独立于平台的方式从 fd 创建 iostream:首先创建一个流缓冲区:boost::iostreams::stream_buffer&lt;boost::iostreams::file_descriptor&gt; streambuf(handle),然后将指向流缓冲区的指针传递给您的新 @ 987654324@.
    【解决方案4】:

    这是一个很好的子类化 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 函数的其他更复杂的解决方案之一。

    【讨论】:

      【解决方案5】:

      如果您不太关心性能,您可以随时从文件中读取所有内容并将其转储到std::stringstream,然后将其传递给您的库。 (或者相反,将所有内容缓冲到字符串流,然后写入 QFile)

      除此之外,两者似乎无法互操作。无论如何,如果编译 Qt 时使用的 STL 版本与您正在使用的 STL 版本有任何不同,那么 Qt 到 STL 之间的操作通常会导致模糊的错误和细微的不一致。例如,如果您更改 Visual Studio 的版本,就会发生这种情况。

      【讨论】:

      • 感谢您的建议,但 stringstream 不是一个选项,因为文件可能会变得非常大。所以将所有数据读入内存是相当难看的。
      猜你喜欢
      • 2011-04-19
      • 1970-01-01
      • 2010-12-20
      • 1970-01-01
      • 1970-01-01
      • 2020-12-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多