如何在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
相关产品推荐
相关产品推荐

