You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C语言中将P6格式PPM文件存入PPM结构体?

问题描述

我需要编写一个可同时存储纯文本PPM文件(P3)和二进制PPM文件(P6)的程序。我不清楚P6格式PPM文件的写入方式,因此无法将其加载到结构体中。目前我已成功实现P3格式PPM文件的加载,请问如何修改现有代码,让它能将P6格式PPM文件存入同一个结构体?还是需要新建结构体来适配P6格式?

以下是我已编写的代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define BUFFER 256

/*------------ Pixel struct ------------*/
struct Pixel {
    int red;
    int green;
    int blue;
};
typedef struct Pixel PIXEL;

/*------------ LinkedList Node struct ------------*/
struct Node {
    char *value;
    struct Node *next;
};
typedef struct Node NODE;

/*------------ PPM struct ------------*/
struct PPM {
    char *Format;
    NODE *comments;
    int commentCounter;
    int width, height;
    int max;
    PIXEL **pixelArray;
};
typedef struct PPM PPM;


/*------------ Function Prototypes ------------*/
PPM* getPPM(FILE *f);
void showPPM(const PPM *img);
PPM* readPPM(const char *filename);
//HELPER FUNCTIONS
char* readLine(FILE *f);
void getComments(FILE *f, PPM *img);


/*------------ getPPM Function ------------*/
PPM* getPPM(FILE * f)
{
    if (f == NULL) { 
        fprintf(stderr,"ERROR! Could not read file"); 
        return NULL;
    } //Checks if image was able to be read
    
    PPM *allocPPM = malloc(sizeof(PPM)); //Allocate memory for PPM struct
    
    //FORMAT
    allocPPM->Format = malloc(3 * sizeof(char));
    allocPPM->Format = readLine(f);
    
    //COMMENTS
    allocPPM->commentCounter = 0;
    getComments(f, allocPPM);
    
    //SIZES
    fscanf(f, "%d", &allocPPM->width);
    fscanf(f, "%d", &allocPPM->height);
    fscanf(f, "%d", &allocPPM->max);
    
    //PIXELS
    allocPPM->pixelArray = calloc(allocPPM->height, sizeof(PIXEL)); //Allocates memory for rows
    for(int i=0 ; i<allocPPM->height ; i++)
    {
        allocPPM->pixelArray[i] = calloc(allocPPM->width, sizeof(PIXEL)); //Allocates memory for columns
        for(int j=0 ; j<allocPPM->width ; j++)
        {
            fscanf(f, "%d", &(allocPPM->pixelArray[i][j].red));
            fscanf(f, "%d", &(allocPPM->pixelArray[i][j].green));
            fscanf(f, "%d", &(allocPPM->pixelArray[i][j].blue));
        }
    }
    
    return allocPPM;    
}

/*------------ getPPM Function ------------*/


/*------------ helper Functions ------------*/
char* readLine(FILE *f)
{
    char dummy[BUFFER]; //Dummy string used so line is not over-allocated
    char *line;
    char chr;
    int count = 0;
    
    chr = getc(f);
    
    if (chr == EOF) { return NULL; } //Empty file checker

    //Populate dummy char by char
    while (chr!='\n' && count < BUFFER-1)
    {
    dummy[count] = chr;
    count++;
    chr = getc(f);
    }
    dummy[count] = '\0';
    
    //Populating line from dummy
    line = calloc((count+1), sizeof(char));
    for(int i=0 ; i<count ; i++)
    {
    line[i] = dummy[i];
    }
    line[count] = '\0';
    return line;
    
    free(dummy); //Free dummy to get rid of overly-allocated string
}

void getComments(FILE *f, PPM *img) //Gets comments from file f and inputs them into PPM struct using LinkedLists
{
    char chr = getc(f);
    while(chr == '#')
    {
        //Gets comment with #
        ungetc(chr, f);
        char *line = readLine(f);
        
        //ADD COMMENT TO LINKED LIST
        if(img->commentCounter == 0) { //Head Node
            img->comments = malloc(sizeof(NODE));
            img->comments->value = line;
            img->commentCounter = 1;
        } else { //Next Node
            NODE *currentNode = img->comments;
            for(int count=1 ; count<img->commentCounter ; count++)
            {
                currentNode = currentNode->next;
            }
            currentNode->next = malloc(sizeof(NODE));
            currentNode->next->value = line;
            img->commentCounter = img->commentCounter + 1;
        }
        //Move to next line
        chr = getc(f);
    }
    //Return to starting position
    ungetc(chr, f);
}

解决方案

不需要新建结构体,现有的PPM和PIXEL结构体完全可以兼容P3和P6格式——两者核心存储的信息(图像尺寸、最大颜色值、每个像素的RGB分量)完全一致,只是像素数据的存储形式不同(文本/二进制)。以下是具体修改步骤:

1. 区分格式处理像素读取

在getPPM函数中,读取完格式、注释、尺寸、最大颜色值后,根据Format字段判断是P3还是P6,分别执行不同的读取逻辑:

  • P3保留原有的文本读取逻辑
  • P6使用二进制读取,注意跳过max值后的空白字符,直接读取单字节的RGB分量

修改后的像素读取代码如下:

//PIXELS
// 修复原内存分配错误:pixelArray是指针数组,需分配PIXEL*类型的空间
allocPPM->pixelArray = calloc(allocPPM->height, sizeof(PIXEL*)); 
for(int i=0 ; i<allocPPM->height ; i++)
{
    allocPPM->pixelArray[i] = calloc(allocPPM->width, sizeof(PIXEL));
    if (strcmp(allocPPM->Format, "P3") == 0) {
        // P3文本格式读取
        for(int j=0 ; j<allocPPM->width ; j++)
        {
            fscanf(f, "%d", &(allocPPM->pixelArray[i][j].red));
            fscanf(f, "%d", &(allocPPM->pixelArray[i][j].green));
            fscanf(f, "%d", &(allocPPM->pixelArray[i][j].blue));
        }
    } else if (strcmp(allocPPM->Format, "P6") == 0) {
        // P6二进制格式读取:先跳过max值后的空白/换行
        int c;
        while ((c = fgetc(f)) != EOF && isspace(c));
        ungetc(c, f);
        
        // 读取单字节RGB分量并转换为int存储
        for(int j=0 ; j<allocPPM->width ; j++)
        {
            unsigned char r, g, b;
            fread(&r, sizeof(unsigned char), 1, f);
            fread(&g, sizeof(unsigned char), 1, f);
            fread(&b, sizeof(unsigned char), 1, f);
            allocPPM->pixelArray[i][j].red = (int)r;
            allocPPM->pixelArray[i][j].green = (int)g;
            allocPPM->pixelArray[i][j].blue = (int)b;
        }
    }
}

2. 修复格式读取的内存泄漏

原代码中提前为Format分配内存后又直接覆盖指针,会导致内存泄漏,直接使用readLine的返回值即可:

//FORMAT
allocPPM->Format = readLine(f);
// 增加格式合法性校验
if (allocPPM->Format == NULL || (strcmp(allocPPM->Format, "P3") != 0 && strcmp(allocPPM->Format, "P6") != 0)) {
    fprintf(stderr, "ERROR! Unsupported PPM format\n");
    free(allocPPM);
    return NULL;
}

3. 注意文件打开模式

打开P6格式文件时必须使用二进制模式,比如:

FILE *fp = fopen("image.p6", "rb");

P3文件可以用文本模式,也可以统一用二进制模式,不影响读取。

4. 补充缺失的头文件与宏定义

原代码缺少必要的头文件和BUFFER宏定义,需在代码开头添加:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define BUFFER 256

内容的提问来源于stack exchange,提问作者Kablooie99

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 13:03:32