【问题标题】:Using QFile made istringstream as binary input (for libpng)使用 QFile 将 istringstream 作为二进制输入(用于 libpng)
【发布时间】:2015-05-06 13:52:59
【问题描述】:

我正在尝试使用 libpng 从 Qt 资源。问题:正在阅读的班级 应该有任何Qt的依赖。

第一步,阅读http://www.piko3d.net/tutorials/libpng-tutorial-loading-png-files-from-streams/#CustomRead我已经写成功了一个函数

read_png(istream& in)

我还成功通过了一个普通的旧 ifstream

ifstream in("abs_path_to_png/icon.png");

read_png(..) 并让它成功读取 png。但是如何获得 来自 Qt 资源的(最好是独立于平台的)istream?表现 不是什么大问题,所以我最初想出了

bool Io_Qt::get_istringstream_from_QFile(QFile& qfile, istringstream& iss)
{
    // [.. Some checking for existence and the file being open ..]
    QString qs(qfile.readAll());
    iss.str(qs.toStdString());

    // I also tried: QByteArray arr(qfile.readAll()); iss.str(arr.data());

    return qfile.isOpen();
}

// Someplace else iss and qfile are created like this:

istringstream iss(std::stringstream::in | std::stringstream::binary);
QFile qfile(":/res/icon.png");
qfile.open(QIODevice::ReadOnly);

这实际上产生了一个问题,乍一看,看起来不错,当说

cout << "'" << iss.str().c_str() << "'" << endl;

我明白了

'�PNG

'

但似乎存在一些空格问题。对于

ifstream in("abs_path_to_png/icon.png");
char c;
cout << "'";
for (int j=0;j<8;j++)
{
    in >> c;
    cout << c;
}
cout << "'" << endl;

产量

'�PNG'

虽然后者有效,但前者的变化最终导致 libpng 检查函数 png_sig_cmp(..) 拒绝我的 png 无效。我的第一个反应是关于“二进制”。然而:

  1. istringstream iss(std::stringstream::in | std::stringstream::binary);感觉不错。
  2. QIODevice::ReadOnly 似乎没有二进制伙伴。

你看到我错过了什么吗?

【问题讨论】:

    标签: c++ qt qfile istringstream


    【解决方案1】:

    您正在处理流,就像它们是带有词法提取运算符的文本数据一样。查看ios::binary 以及readwrite 方法,这些方法适用于处理二进制流。

    在你的情况下,我会完全放弃 operator&lt;&lt;operator&gt;&gt;,转而支持 readwrite。使用ostream::write 写入从QIODevice::readAll() 返回的字节数组数据,以将其内容传输到您的临时字符串流,例如,并在您的测试中使用ostream::read 来验证其内容。

    确保正确传输的一个很好的测试用例是编写一个测试,从 QFile 读取内容,使用ostream::write 将其传输到二进制输出文件流 (ofstream),然后尝试把它加载到图像软件中,看看它是否可以。然后将您的文件流与字符串流交换,并在您工作时将其传递给libpng

    【讨论】:

    • 感谢您的提示。我猜有效性的测试是 png_sig_cmp(..) 是否接受流内容。它适用于我的 ifstream 方法。它不适用于 istringstream 之一。在这两个工作流程中,没有“>”运算符进入其中,对于 iss.str(..): 在我的第一个中使用 QString(qfile.readAll()) 或 QByteArray(qfile.readAll( )) 在我的第二次尝试中。无济于事。自然地,我要么触摸流以进行输出(使用“>”)或发送到 libpng。不能同时进行。
    • 试试这样的——QByteArray bytes = qfile.readAll(); vector&lt;char&gt; buf(bytes.size()); bytes.read(&amp;buf[0], buf.size()); ofstream out_file("my_test.png", ios::binary); out_file.write((const char*)&amp;buf[0], buf.size());
    • 然后看看"my_test.png"是否在图像软件中正常打开。如果是这样,您就知道从QIODevice 转移到ostream 对象的逻辑可以正常工作(此时可以使用字符串流),如果其余部分工作正常,其余部分应该很简单。
    • 我想知道的一件事:为什么不跳过 Qt 中间人,直接在fstream 中打开 png?然后,您不必先将字节从QIODevice 传输到stringstream。是不是因为您的外部代码不可避免地必须将 PNG 加载到 QFile 中?如果是这样,上述解决方案应该可以将内容从 QFile 传输到 ostream,前提是我没有犯错。
    • 经过一些试验和错误之后,我终于让它工作了——基于你关于读写的原始提示。我会发布一个摘要。
    【解决方案2】:

    正如艾克所说,这似乎确实是关于 以文本为中心的运算符 '>>'、'

    首先我在 QFile 旁边使用了一个纯字符串流:

    // Explicitly setting flags should at least contain ::in and ::out
    // stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary)
    // However, the default constructor is perfectly fine.
    stringstream ss;
    QFile qfile(":/res/icon.png");
    qfile.open(QIODevice::ReadOnly);
    

    这是我传递给我的函数的,现在看起来像这样:

    bool Io_Qt::get_stringstream_from_QFile(QFile& qfile, stringstream& ss)
    {
        // [.. some sanity checks..]
        QDataStream in(&qfile);
        uint len = qfile.size();
        char* c = (char*)malloc(len*sizeof(char));
        in.readRawData(c,len);
        ss.write(c,len);
        free (c);
        return true;
    }
    

    此流已被填满,并且大小合适。特别是从 .write(..) 写入所需的字符数 数据中有多少个零。我最大的问题是 我不愿意同时拥有 std::stringstream::in 和 std::stringstream::out 同时激活,因为 组合对我来说似乎有些古怪。然而两者都是需要的。 但是,我发现我可能会跳过 std::stringstream::binary。 但因为它似乎没有造成任何伤害,我喜欢 保留它以求好运。不过,请随意评论这个迷信! :-)

    【讨论】:

    • 我可能是错的,但我会认为相反:二进制标志是必需的,但 stringstream::instringstream::out 不一定,因为这应该是 stringstream 的默认状态(输入和输出)而不是istringstreamostringstream
    • 我也是这么想的。现在我也单独测试了二进制文件。但是:我在结果中看到就像我的帖子中所说的那样:输入和输出都是必需的。由于某种原因,二进制不是。有问题的 png 是 32x32,每像素 3 个字节,颜色类型 2,由我使用 GIMP 手绘。这一切都发生在 64 位 Debian 机器上。
    • 这很奇怪——让我很困惑,也许是特定于编译器的。哦,好吧,指定它们不会有什么坏处!通常我发现 iostreams 是如此违反直觉,以至于我非常间接地使用它们,或者有时只是偏爱 C 函数。这是我无法给你一个完美答案的原因之一,但我很高兴你能成功。
    • 不,我明白了!我实际上通过按照帖子中所述的方式调用构造函数来强制模式。如果我只是像在 stringstream ss; 中那样初始化默认值,它也可以正常工作。 :-) 这仍然不能解释 std::stringstream::binary 的不必要性。 :-/
    【解决方案3】:

    一个更干净、更少 C 风格、更多 Qt/C++ 风格的版本可以是:

    QFile file(filePath);
    file.open(QIODevice::ReadOnly);
    QByteArray data = file.readAll();
    std::istringstream iss(data.toStdString());
    

    现在使用iss,在我的情况下这是用于libTIFF:

    TIFF* tif = TIFFStreamOpen("MemTIFF", &iss);
    // ...
    

    此外,对于 PNG,您现在可以关注您的 already posted article,因为 std::istringstream 的类型为 std::istream

    请注意,此解决方案涉及将文件数据完全加载到内存中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-12-18
      • 2020-10-01
      • 2011-12-20
      • 1970-01-01
      • 2015-04-17
      • 2021-01-31
      • 1970-01-01
      相关资源
      最近更新 更多