Продолжая мой предыдущий комментарий, у вас есть несколько проблем, связанных с обработкой Обычного формата файла PGM , который помешает вашему успешному чтению файла.
Во-первых, fgets(PGMcheck, sizeof(PGMcheck), inFile);
не гарантируется правильное чтение PGMcheck
. За магическим числом может следовать "(blanks, TABs, CRs, LFs)"
, поэтому fgets
будет читать больше, чем просто магическое число , если за ним не следует один '\n'
- не гарантируетсяпо формату. Хотя fgets()
, как правило, является правильным способом для строчно-ориентированного ввода, формат PGM не обязательно должен быть отформатирован в строках, поэтому вам остается использовать функцию отформатированный ввод или символьнуюиндивидуальный подход.
(вы можете использовать fgets()
, но для этого потребуется анализ результирующего буфера и сохранение любой части буфера, расширенной за пределы magic-number , чтобы включить в качестве начальной части следующегочитать)
Вы исправили свою попытку сравнения строк, используя !=
вместо strcmp
, но вы все равно должны сравнить магическое число с "P2"
для чтения на обычном языке. -PGM формат файла (так как ваш вопрос изначально был включен) Идите вперед и прочитайте magic-number в строку, но используйте функцию форматированного ввода (fscanf
), чтобы читать только до тех пор, пока первый пробел не станетвстречаются независимо от того, что это за пробел.
Наконец, нет необходимости хранить магическое число как часть структуры plain_pgm
. Это то, что вы проверяете , прежде чем пытаться заполнить структуру. Это либо "P2"
, либо нет - не нужно его хранить.
Для переносимости рекомендуется использовать типы точной ширины для хранения при чтении изображенияфайлы. Есть ряд преимуществ, но главное, ваша программа будет работать правильно, независимо от того, запущена она на вашем x86_64 или на вашем чипе TI-MSP432. Точные типы ширины определены в stdint.h
, а макросы от печати и чтения точных типов ширины представлены в inttypes.h
. Вместо char
у вас есть int8_t
, вместо unsigned char
у вас есть uint8_t
и т. Д., Где числовое значение указывает точное число байтов для типа.
При этом ваш pgmСтруктура может выглядеть следующим образом:
typedef struct { /* struct for plain pgm image */
uint32_t w, h; /* use exact width types for portable code */
uint16_t max; /* 16-bit max */
uint16_t **pixels; /* pointer-to-pointer for pixel values */
} plain_pgm;
Ваше распределение в значительной степени корректно, но переставляет так, чтобы возвращать указатель-на-указатель на uint16_t
(достаточно для значений maximum gray value
пикселей), вы можете сделать:
uint16_t **alloc_pgm_pixels (uint32_t w, uint32_t h)
{
uint16_t **pixels = NULL;
/* allocate/validate height number of pointers */
if (!(pixels = malloc (h * sizeof *pixels))) {
perror ("malloc-pixels");
return NULL;
}
/* allocate/validate width number of values per-pointer */
for (uint32_t i = 0; i < h; i++)
if (!(pixels[i] = malloc (w * sizeof *pixels[i]))) {
perror ("malloc-pixels[i]");
return NULL;
}
return pixels; /* return allocated pointers & storage */
}
Ваша функция чтения требует большой помощи. Во-первых, вы обычно хотите открыть и проверить файл, открытый для чтения в вызывающей функции, и передать указатель open FILE *
в качестве параметра функции чтения, а не имени файла. (если файл не может быть открыт в вызывающей программе, нет необходимости выполнять вызов функции для начала). С этим изменением и передачей указателя на вашу структуру ваша функция чтения может выглядеть следующим образом:
int read_pgm (FILE *fp, plain_pgm *pgm)
{
char buf[RDBUF]; /* buffer for magic number */
uint32_t h = 0, w = 0; /* height/width counters */
if (fscanf (fp, "%s", buf) != 1) { /* read magic number */
fputs ("error: invalid format - magic\n", stderr);
return 0;
}
if (strcmp (buf, MAGIC_PLN) != 0) { /* validate magic number */
fprintf (stderr, "error: invalid magic number '%s'.\n", buf);
return 0;
}
/* read pgm width, height, max gray value */
if (fscanf (fp, "%" SCNu32 " %" SCNu32 " %" SCNu16,
&pgm->w, &pgm->h, &pgm->max) != 3) {
fputs ("error: invalid format, h, w, max or included comments.\n",
stderr);
return 0;
}
/* validate allocation of pointers and storage for pixel values */
if (!(pgm->pixels = alloc_pgm_pixels (pgm->w, pgm->h)))
return 0;
for (;;) { /* loop continually until image read */
if (fscanf (fp, "%" SCNu16, &pgm->pixels[h][w]) != 1) {
fputs ("error: stream error or short-read.\n", stderr);
return 0;
}
if (++w == pgm->w)
w = 0, h++;
if (h == pgm->h)
break;
}
return 1;
}
( примечание: эта функция чтения НЕ учитывает строки комментариев реализация игнорирования строк комментариев оставлена на ваше усмотрение. Вы можете сделать дополнительный вызов к fscanf
до и между чтением каждой части магического числа, ширины, высоты и максимального значения серого с чем-то похожим на " # %[^\n']"
дляпропустите любое количество пробелов и прочитайте до и включая следующий '#'
символ и до конца строки, или просто используйте fgetc
в цикле поиска следующего непробельного символа и проверки, является ли он '#'
и если нет, используйте ungetc
, если это так, очистите до конца строки.)
В целом, например, вы можете сделать:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#define RDBUF 32 /* if you need a constant, #define one (or more) */
#define MAGIC_PLN "P2"
typedef struct { /* struct for plain pgm image */
uint32_t w, h; /* use exact width types for portable code */
uint16_t max; /* 16-bit max */
uint16_t **pixels; /* pointer-to-pointer for pixel values */
} plain_pgm;
uint16_t **alloc_pgm_pixels (uint32_t w, uint32_t h)
{
uint16_t **pixels = NULL;
/* allocate/validate height number of pointers */
if (!(pixels = malloc (h * sizeof *pixels))) {
perror ("malloc-pixels");
return NULL;
}
/* allocate/validate width number of values per-pointer */
for (uint32_t i = 0; i < h; i++)
if (!(pixels[i] = malloc (w * sizeof *pixels[i]))) {
perror ("malloc-pixels[i]");
return NULL;
}
return pixels; /* return allocated pointers & storage */
}
int read_pgm (FILE *fp, plain_pgm *pgm)
{
char buf[RDBUF]; /* buffer for magic number */
uint32_t h = 0, w = 0; /* height/width counters */
if (fscanf (fp, "%s", buf) != 1) { /* read magic number */
fputs ("error: invalid format - magic\n", stderr);
return 0;
}
if (strcmp (buf, MAGIC_PLN) != 0) { /* validate magic number */
fprintf (stderr, "error: invalid magic number '%s'.\n", buf);
return 0;
}
/* read pgm width, height, max gray value */
if (fscanf (fp, "%" SCNu32 " %" SCNu32 " %" SCNu16,
&pgm->w, &pgm->h, &pgm->max) != 3) {
fputs ("error: invalid format, h, w, max or included comments.\n",
stderr);
return 0;
}
/* validate allocation of pointers and storage for pixel values */
if (!(pgm->pixels = alloc_pgm_pixels (pgm->w, pgm->h)))
return 0;
for (;;) { /* loop continually until image read */
if (fscanf (fp, "%" SCNu16, &pgm->pixels[h][w]) != 1) {
fputs ("error: stream error or short-read.\n", stderr);
return 0;
}
if (++w == pgm->w)
w = 0, h++;
if (h == pgm->h)
break;
}
return 1;
}
int main (int argc, char **argv) {
plain_pgm pgm = { .w = 0 }; /* plain_pgm struct instance */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
if (!read_pgm (fp, &pgm)) { /* validate/allocate/read pgm file */
fputs ("error: read_pgm failed.\n", stderr);
return 1;
}
if (fp != stdin) /* close file if not stdin */
fclose (fp);
/* output success */
printf ("successful read of '%s'\n%" PRIu32 "x%" PRIu32 " pixel values.\n",
argc > 1 ? argv[1] : "stdin", pgm.w, pgm.h);
for (uint32_t i = 0; i < pgm.h; i++) /* free pixel storage */
free (pgm.pixels[i]);
free (pgm.pixels); /* free pointers */
}
Пример Использование/ Вывод
Используя пример apollonian_gasket.ascii.pgm, изображения размером 600 х 600 с аполлоновой прокладкой в качестве тестового файла, вы получите:
$ ./bin/read_pgm_plain dat/apollonian_gasket.ascii.pgm
successful read of 'dat/apollonian_gasket.ascii.pgm'
600x600 pixel values.
ЯMory Использование / Проверка ошибок
В любом написанном вами коде, который динамически распределяет память, у вас есть 2 обязанности в отношении любого выделенного блока памяти: (1) всегда сохраняйте указатель на начальный адрес для блокапамяти, таким образом, (2) он может быть освобожден , когда он больше не нужен.
Обязательно, чтобы вы использовали программу проверки ошибок памяти, чтобы гарантировать, что вы не пытаетесь получить доступ к памятиили написать за пределами / за пределами выделенного блока, попытаться прочитать или основать условный переход на неинициализированном значении и, наконец, подтвердить, что вы освобождаете всю выделенную память.
Для Linux valgrind
является нормальным выбором. Для каждой платформы есть похожие проверки памяти. Все они просты в использовании, просто запустите вашу программу через него.
$ valgrind ./bin/read_pgm_plain dat/apollonian_gasket.ascii.pgm
==8086== Memcheck, a memory error detector
==8086== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==8086== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==8086== Command: ./bin/read_pgm_plain dat/apollonian_gasket.ascii.pgm
==8086==
successful read of 'dat/apollonian_gasket.ascii.pgm'
600x600 pixel values.
==8086==
==8086== HEAP SUMMARY:
==8086== in use at exit: 0 bytes in 0 blocks
==8086== total heap usage: 604 allocs, 604 frees, 730,472 bytes allocated
==8086==
==8086== All heap blocks were freed -- no leaks are possible
==8086==
==8086== For counts of detected and suppressed errors, rerun with: -v
==8086== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Всегда подтверждайте, что вы освободили всю выделенную память и что ошибок памяти нет.
Просмотритевнесенные изменения, и если вы не понимаете, почему что-то не было сделано, оставьте комментарий с вопросом, и я буду рад помочь вам.