【问题标题】:How to read and write a ppm file?如何读写 ppm 文件?
【发布时间】:2025-12-21 16:15:07
【问题描述】:

我尝试读取一个 ppm 文件并创建一个相同的新文件。但是当我用 GIMP2 打开它们时,图像就不一样了。

我的代码哪里出了问题?

int main()
{
    FILE *in, *out;
    in = fopen("parrots.ppm","r");
    if( in == NULL )
    {
        std::cout<<"Error.\n";
        return 0;
    }

    unsigned char *buffer = NULL;

    long size = 0;
    fseek(in, 0, 2);
    size = ftell(in);
    fseek(in, 0, 0);

    buffer = new unsigned char[size];
    if( buffer == NULL )
    {
        std::cout<<"Error\n";
        return 0;
    }

    if( fread(buffer, size, 1, in) < 0 )
    {
          std::cout<<"Error.\n";
          return 0 ; 
    }

    out = fopen("out.ppm","w");
    if( in == NULL )
    {
         std::cout<<"Error.\n";
         return 0;
    }

    if( fwrite(buffer, size, 1, out) < 0 )
    {
         std::cout<<"Error.\n";
         return 0;
    }

    delete[] buffer;

    fcloseall();

    return 0;
}

在此之前,我在一个结构中读取 ppm 文件,当我编写它时,我得到了相同的图像,但绿色比原始图片中的更强烈。然后我尝试了这个简单的阅读和写作,但我得到了相同的结果。

【问题讨论】:

标签: c++ ppm


【解决方案1】:
int main()

缺少包含。

FILE *in, *out;

C++ 程序中的 C 风格 I/O,为什么?另外,在初始化时声明,接近第一次使用。

in = fopen("parrots.ppm","r");

这是以文本模式打开文件,这肯定不是您想要的。使用"rb" 表示模式。

unsigned char *buffer = NULL;

在初始化时声明,接近首次使用。

fseek(in, 0, 2);

你应该使用SEEK_END不保证定义为2

fseek(in, 0, 0);

见上文,SEEK_SET 不保证定义为 0。

buffer = new unsigned char[size];
if( buffer == NULL )

默认情况下,new 不会返回 NULL 指针,而是抛出 std::bad_alloc 异常。 (由于过度分配是大多数当前操作系统的常态,即使使用 malloc(),检查 NULL 也不会保护您免受内存不足的影响,但很高兴看到您养成了检查的习惯。)

C++11 为我们带来了smart pointers。使用它们。它们是避免内存泄漏(C++ 极少数弱点之一)的绝佳工具。

if( fread(buffer, size, 1, in) < 0 )

成功使用fread应该返回写入对象的数量,应该检查它是否等于第三个参数(!= 1),而不是&lt; 0

out = fopen("out.ppm","w");

又是文字模式,这里要"wb"

if( fwrite(buffer, size, 1, out) < 0 )

参见上面关于fread 返回值的注释。此处同样适用。

fcloseall();

不是标准功能。使用fclose( in );fclose( out );


一个 C++11 化的解决方案(为了简洁省略错误检查)看起来有点像这样:

#include <iostream>
#include <fstream>
#include <memory>

int main()
{
    std::ifstream in( "parrots.ppm", std::ios::binary );
    std::ofstream out( "out.ppm", std::ios::binary );

    in.seekg( 0, std::ios::end );
    auto size = in.tellg();
    in.seekg( 0 );

    std::unique_ptr< char[] > buffer( new char[ size ] );

    in.read( buffer.get(), size );
    out.write( buffer.get(), size );

    in.close();
    out.close();

    return 0;
}

当然,smart 解决方案会通过Boost.Filesystemstandard functionality (在撰写本文时为实验性)进行实际的文件系统复制。

【讨论】:

    最近更新 更多