对前一篇写的函数进行封装和代码提炼
peek函数(C语言是没有这个函数,自己写的,类似于C++里面的peek函数)
int peek(FILE *fp) {
// todo 判空未做
// 保存当前文件指针的位置
fpos_t pos;
fgetpos(fp, &pos);
int ch;
// 读取文件指针当前指向的字符,文件指针的位置自动指向下一个位置
// 类似于其它高级语言里面的迭代器
ch = fgetc(fp);
fsetpos(fp, &pos);
return ch;
}
skip_comment函数(用来跳过换行和注释信息)
void skip_comment(FILE *fp) {
// 检测当前字符是注释开或者是换行,后面的判断是过滤多个空行
while (peek(fp) == '#'||peek(fp) == '\n') {
// 过滤注释信息,直到换行
while (fgetc(fp) != '\n');
}
}
swap_two_byte函数(用来交换双字节存放的值)
// 移位版
unsigned short swap_two_byte(unsigned short data) {
return ((0xff & data) << 8) | ((0xff00 & data) >> 8);
}
// 指针版
void swap_two_byte_pointer(unsigned short *ptr) {
// 取出第一个字节数据
unsigned char tmp = *((unsigned char *) ptr);
*((unsigned char *) ptr) = *((unsigned char *) ptr + 1);
*((unsigned char *) ptr + 1) = tmp;
}
typedef struct {
char model[3]; // 模式 P2和P5
unsigned width; // 宽度
unsigned height; // 高度
unsigned max; // 最大值
// 针对P5格式
unsigned byte_num; // 字节数
unsigned *data; // 数据(之前是二维数组,这里是直接用的一级指针;所谓的二维数组,实际在内存中也是一个连续一维数组)
} PGM_TypeDef;
// C语言标准规定,当数组名作为数组定义的标识符(也就是定义或声明数组时)、sizeof 或 & 的操作数时,它才表示整个数组本身,在其他的表达式中,数组名会被转换为指向第 0 个元素的指针(地址)。
void pgm_p2_write(const PGM_TypeDef *pgm, const char *fileName) {
FILE *fp = fopen(fileName, "w");
if (fp == NULL) exit(-1);
fprintf(fp, "%s\n", pgm->model);
fprintf(fp, "%d %d\n", pgm->width, pgm->height);
// max value
fprintf(fp, "%d\n", pgm->max);
// 数据
for (int i = 0; i < pgm->width; ++i) {
for (int j = 0; j < pgm->height; ++j) {
fprintf(fp, "%d ", pgm->data[i + j]);
}
fputc('\n', fp);
}
fclose(fp);
}
PGM_TypeDef *pgm_p2_read(PGM_TypeDef *pgm, const char *fileName, FILE *fp) {
// 开辟内存空间
pgm->data = (unsigned *) malloc(sizeof(unsigned) * pgm->height * pgm->width);
// 初始化数据
memset(pgm->data, 0, sizeof(unsigned) * pgm->height * pgm->width);
for (int i = 0; i < pgm->width; ++i) {
for (int j = 0; j < pgm->height; ++j) {
fscanf(fp, "%d", &pgm->data[i + j]);
}
}
return pgm;
}
void pgm_p5_write(const PGM_TypeDef *pgm, const char *fileName) {
FILE *fp = fopen(fileName, "wb+");
if (fp == NULL) exit(-1);
fprintf(fp, "%s\n", pgm->model);
fprintf(fp, "%d %d\n", pgm->width, pgm->height);
fprintf(fp, "%d\n", pgm->max);
for (int i = 0; i < pgm->width; ++i) {
for (int j = 0; j < pgm->height; ++j) {
unsigned value = (i + j) % pgm->max == 0 ? pgm->max : (i + j) % pgm->max;
if (pgm->byte_num == 2) value = swap_two_byte(value);
fwrite(&value, pgm->byte_num, 1, fp);
}
}
fclose(fp);
}
PGM_TypeDef *pgm_p5_read(PGM_TypeDef *pgm, const char *fileName, FILE *fp) {
skip_comment(fp);
if (pgm->max > UCHAR_MAX) {
pgm->byte_num = 2;
} else {
pgm->byte_num = 1;
}
pgm->data = (unsigned *) malloc(sizeof(unsigned) * pgm->height * pgm->width);
for (int i = 0; i < pgm->width; ++i) {
for (int j = 0; j < pgm->height; ++j) {
unsigned data;
fread(&data, pgm->byte_num, 1, fp);
if (pgm->byte_num == 2) data = swap_two_byte(data); // 移位操作
pgm->data[i + j] = data;
}
}
return pgm;
}
void pgm_p2_to_p5_write(PGM_TypeDef *pgm, const char *pgm2_file, const char *pgm5_file) {
pgm_read(pgm, pgm2_file);
// P5 格式
strcpy(pgm->model, "P5");
pgm_write(pgm, pgm5_file);
}
void pgm_p5_to_p2_write(PGM_TypeDef *pgm, const char *pgm5_file, const char *pgm2_file) {
pgm_read(pgm, pgm5_file);
// P2 格式
strcpy(pgm->model, "P2");
pgm_write(pgm, pgm2_file);
}
void pgm_write(PGM_TypeDef *pgm, const char *fileName) {
if (strcmp(pgm->model, "P2") == 0) {
pgm_p2_write(pgm, fileName);
} else {
pgm->byte_num = pgm->max > UCHAR_MAX ? 2 : 1;
pgm_p5_write(pgm, fileName);
}
}
PGM_TypeDef *pgm_read(PGM_TypeDef *pgm, const char *fileName) {
// 模式只能选择 rb,只选r的话,会读取错误;应该是在只有r的情况下,window会自动在换行符那里进行处理
FILE *fp = fopen(fileName, "rb");
// fscanf(fp,"%s",pgm->model);
fgets(pgm->model, 3, fp);
// 跳过空格或注释
skip_comment(fp);
fscanf(fp, "%d %d", &pgm->width, &pgm->height);
// 跳过空格或注释
skip_comment(fp);
fscanf(fp, "%d", &pgm->max);
char default_mode[3] = {"P2"};
// 跳过空格或注释
skip_comment(fp);
PGM_TypeDef *ptr;
if (strcmp("P2", pgm->model) == 0) {
ptr = pgm_p2_read(pgm, fileName, fp);
} else {
ptr = pgm_p5_read(pgm, fileName, fp);
}
fclose(fp);
return ptr;
}
// 打印数据
void pgm_data_print(PGM_TypeDef *pgm) {
for (int i = 0; i < pgm->width; ++i) {
for (int j = 0; j < pgm->height; ++j) {
printf("%d ", pgm->data[i + j]);
}
printf("\n");
}
}
// 创建数据
void pgm_data_create(PGM_TypeDef *pgm) {
pgm->data = (unsigned *) malloc(sizeof(unsigned) * pgm->width * pgm->height);
for (int i = 0; i < pgm->width; ++i) {
for (int j = 0; j < pgm->height; ++j) {
pgm->data[i + j] = (i + j) % pgm->max == 0 ? pgm->max : (i + j) % pgm->max;
}
}
}
// 释放开辟的内存
void pgm_destroy(PGM_TypeDef *pgm) {
if (pgm->data != NULL){
free(pgm->data);
pgm->data = NULL;
}
}
int main() {
PGM_TypeDef pgm = {"P5", 200, 300, 257, 1};
pgm_data_create(&pgm);
pgm_write(&pgm, "test_p5.pgm");
pgm_destroy(&pgm);
PGM_TypeDef PGM_Read;
pgm_read(&PGM_Read, "test_p5.pgm");
pgm_data_print(&PGM_Read);
free(PGM_Read.data);
// PGM_TypeDef pgm;
// pgm_p2_to_p5_write(&pgm, "test_p2.pgm", "test_p55.pgm");
// pgm_p5_to_p2_write(&pgm, "test_p55.pgm", "test_p222.pgm");
// pgm_data_print(&pgm);
return 0;
}