I am attempting to read a '.raw' file which stores the contents of an image that was taken on a camera using C. I would like to store these contents into a uint16_t *.
In the following code I attempt to store this data into a pointer, using fread(), and then write this data into a test file, using fwrite(), to check if my data was correct.
However, when I write the file back it is completely black when I check it.
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#define MAX_ROW 2560
#define MAX_COL 2160
int main()
{
char filename[32] = "image1.raw";
FILE * image_raw = fopen(filename, "rb");
fseek(image_raw, 0, 2);
long filesize = ftell(image_raw);
/*READ IMAGE DATA*/
uint16_t * image_data_ptr;
image_data_ptr = (uint16_t *)malloc(sizeof(uint16_t)*MAX_ROW*MAX_COL);
fread(image_data_ptr, sizeof(uint16_t), filesize, image_raw);
fclose(image_raw);
/*TEST WRITING THE SAME DATA BACK INTO TEST RAW FILE*/
FILE *fp;
fp = fopen("TEST.raw", "w");
fwrite(image_data_ptr, sizeof(uint16_t), filesize, fp);
fclose(fp);
return 0;
}
There are multiple issues with your code:
lack of error handling.
not seeking the input file back to offset 0 after seeking it to get its size. Consider using stat() or equivalent to get the file size without having to seek the file at all.
not dividing filesize by sizeof(uint16_t) when reading from the input file, or writing to the output file. filesize is expressed in bytes, but fread/fwrite are expressed in number of items of a given size instead, and your items are not 1 byte in size.
not opening the output file in binary mode.
leaking the buffer you allocate.
With that said, try something more like this instead:
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
int main()
{
char filename[32] = "image1.raw";
FILE *image_raw = fopen(filename, "rb");
if (!image_raw) {
fprintf(stderr, "Can't open input file\n");
return -1;
}
if (fseek(image_raw, 0, SEEK_END) != 0) {
fprintf(stderr, "Can't seek input file\n");
fclose(image_raw);
return -1;
}
long filesize = ftell(image_raw);
if (filesize == -1L) {
fprintf(stderr, "Can't get input file size\n");
fclose(image_raw);
return -1;
}
rewind(image_raw);
long numSamples = filesize / sizeof(uint16_t);
/*READ IMAGE DATA*/
uint16_t *image_data_ptr = (uint16_t*) malloc(filesize);
if (!image_data_ptr) {
fprintf(stderr, "Can't allocate memory\n");
fclose(image_raw);
return -1;
}
size_t numRead = fread(image_data_ptr, sizeof(uint16_t), numSamples, image_raw);
if (numRead != numSamples) {
fprintf(stderr, "Can't read samples from file\n");
free(image_data_ptr);
fclose(image_raw);
return -1;
}
fclose(image_raw);
/*TEST WRITING THE SAME DATA BACK INTO TEST RAW FILE*/
FILE *fp = fopen("TEST.raw", "wb");
if (!fp) {
fprintf(stderr, "Can't open output file\n");
free(image_data_ptr);
return -1;
}
if (fwrite(image_data_ptr, sizeof(uint16_t), numSamples, fp) != numSamples) {
fprintf(stderr, "Can't write to output file\n");
fclose(fp);
free(image_data_ptr);
return -1;
}
fclose(fp);
free(image_data_ptr);
return 0;
}
You have already a great answer and useful comments
anyway, consider that if you want to iterate over your file, loaded in memory as a whole, as an array of unsigned words:
if the file size could be odd what to do at the last byte/word
you may read the file as a whole in a single call, after having the file size determined
fstat() is the normal way to get the file size
get the file name from the command line as an argument is much more flexible than recompile the program or change the file name in order to use the program
The code below does just that:
uses image.raw as a default for the file name, but allowing you to enter the file name on the command line
uses fstat() to get the file size
uses a single fread() call to read the entire file as a single record
A test using the original program file as input:
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 20/07/2021 17:40 1067 main.c
PS > gcc -Wall -o tst main.c
PS > ./tst main.c
File is "main.c". Size is 1067 bytes
File "main.c" loaded in memory.
PS > ./tst xys
File is "xys". Could not open: No such file or directory
The C example
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
int main(int argc, char**argv)
{
const char* default_file = "image.raw";
char f_name[256];
if (argc < 2)
strcpy(f_name, default_file);
else
strcpy(f_name, argv[1]);
FILE* F = fopen(f_name, "rb");
if (F == NULL)
{
printf("File is \"%s\". ", f_name);
perror("Could not open");
return -1;
}
struct stat info;
fstat(_fileno(F),&info);
printf("File is \"%s\". Size is %lu bytes\n", f_name, info.st_size);
uint16_t* image = malloc(info.st_size);
if (image == NULL)
{ perror("malloc() error");
return -2;
};
if (fread(image, info.st_size, 1, F) != 1)
{ perror("read error");
free(image);
return -3;
};
// use 'image'
printf("File \"%s\" loaded in memory.\n", f_name);
free(image);
fclose(F);
return 0;
}
Related
I wrote this code to recover 50 images from cs50 pset4 recover. The code can only retrieve 4 images all of which are not the right ones. It is compiling fine. When i use printf to debug it seems like the if(found) piece of code runs many times when name_count == 0 more than it is supposed to.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
typedef uint8_t BYTE;
const int BLOCK_SIZE = 152;
bool is_a_jpeg(BYTE *buffer);
int main(int argc, char *argv[])
{
//Check if there are exactly two commandline arguments
if (argc != 2)
{
printf("usage: ./IMAGE\n");
return 1;
}
//Open a storage device and chack if it has data
FILE *input = fopen(argv[1], "r");
if (input == NULL)
{
fclose(input);
printf("Could not open file.\n");
return 2;
}
//Create a file to write into and clear it
FILE *img = NULL;
//Declar an interger of counting images
int name_count = 0;
// create buffer
BYTE buffer[BLOCK_SIZE];
//Declare space for saving the filename
char filename[8];
//Declare a bolean variable used to check for already found images
bool found = false;
//A function for reading through the device looking for images
while (fread(buffer, BLOCK_SIZE, 1, input))
{
//If a jpeg image is found notify the program(set found = true)
//and start writing the data to a new file
if (is_a_jpeg(buffer))
{
found = true;
//If we are not writing the first image, close the previous one
if (name_count > 0)
{
fclose(img);
}
//Create incrementing filenames for each new picture i.e 000.jpg, 001.jpg etc.
sprintf(filename, "%03d.jpg", name_count);
//Open an initially created empty file and start writing to it
img = fopen (filename, "w");
name_count++;
fwrite(buffer, BLOCK_SIZE, 1, img);
}
//Continue writing to a file as soon as it is found until another JPEG image is found
if(found)
{
fwrite(buffer, BLOCK_SIZE, 1, img);
}
}
//Close all the files
fclose(input);
fclose(img);
return 0;
}
//Function to check for a JPEG Image
bool is_a_jpeg(BYTE *buffer)
{
return buffer[0] == 0xff &&
buffer[1] == 0xd8 &&
buffer[2] == 0xff &&
(buffer[3] & 0xf0) == 0xe0;
}
cs50 results for check50
printing dot everytime if(found) code runs. The code unnecessarily spends a lot of time on the first image before closing it
There are some problems in the posted code:
the block size should be 512 bytes, not 152.
you should not fclose(file) if fopen failed. This has undefined behavior.
you unconditionally close img at the end, which may have undefined behavior if no JPG file was found or if the last JPG file could not be open.
both the disk image file and the jpg destination files must be open in binary mode. This may explain unexpected behavior if you are working on Windows.
Here is a modified version:
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
typedef uint8_t BYTE;
const int BLOCK_SIZE = 512;
bool is_a_jpeg(const BYTE *buffer);
int main(int argc, char *argv[]) {
//Check if there are exactly two commandline arguments
if (argc != 2) {
printf("usage: ./IMAGE\n");
return 1;
}
//Open a storage device and check if it has data
FILE *input = fopen(argv[1], "rb");
if (input == NULL) {
fprintf(stderr, "Could not open file %s: %s.\n", argv[1], strerror(errno));
return 2;
}
//Create a file to write into and clear it
FILE *img = NULL;
//Declare an integer of counting images
int name_count = 0;
// create buffer
BYTE buffer[BLOCK_SIZE];
//Declare space for saving the filename
char filename[16];
//A function for reading through the device looking for images
while (fread(buffer, BLOCK_SIZE, 1, input)) {
//If a jpeg image is found notify the program(set found = true)
//and start writing the data to a new file
if (is_a_jpeg(buffer)) {
//close the current image file if any
if (img) {
fclose(img);
img = NULL;
}
//Create incrementing filenames for each new picture i.e 000.jpg, 001.jpg etc.
sprintf(filename, "%03d.jpg", name_count);
name_count++;
//Create an empty file and start writing to it
img = fopen(filename, "wb");
if (img == NULL) {
fprintf(stderr, "Could not open output file %s: %s.\n", filename, strerror(errno));
}
}
//Continue writing to a file as soon as it is found until another JPEG image is found
if (img) {
if (!fwrite(buffer, BLOCK_SIZE, 1, img)) {
fprintf(stderr, "Error writing to %s: %s.\n", filename, strerror(errno));
fclose(img);
img = NULL;
}
}
}
//Close all the files
fclose(input);
if (img)
fclose(img);
return 0;
}
//Function to check for a JPEG Image
bool is_a_jpeg(const BYTE *buffer) {
return buffer[0] == 0xff &&
buffer[1] == 0xd8 &&
buffer[2] == 0xff &&
(buffer[3] & 0xf0) == 0xe0;
}
I am trying to read from a pdf file and write into another file where I run to the problem.
In the while loop, fread reads only 589 bytes which is expected to be 1024 for the first time.
In the second loop, fread reads 0 bytes.
I am sure that the pdf file is beyond 1024 bytes.
Here is a similar problem. The phenomenon is the same. But I do not use strlen() which causes that problem.
So how can I resolve the problem?
My code is here:
#include <stdio.h>
#define MAXLINE 1024
int main() {
FILE *fp;
int read_len;
char buf2[MAXLINE];
FILE *fp2;
fp2 = fopen("test.pdf", "w");
if ((fp = fopen("LearningSpark.pdf", "r")) == NULL) {
printf("Open file failed\n");
}
while ((read_len = fread(buf2, sizeof(char), MAXLINE, fp)) > 0) {
int write_length = fwrite(buf2, sizeof(char), read_len, fp2);
if (write_length < read_len) {
printf("File write failed\n");
break;
}
}
return 0;
}
fopen(filename, "r") is system dependent. See this post on what may happen to the data you read if you are on Windows, for example. Basically it is related to how certain characters are translated on different systems in text mode, ie., \n is "End-of-Line" on Unix-type systems, but on Windows it is \r\n.
Important: On Windows, ASCII char 27 will result in End-Of-File, if reading in text mode, "r", causing the fread() to terminate prematurely.
To read a binary file, use the "rb" specifier. Similarly for "w", as mentioned here, you should use "wb" to write binary data.
Binary files such as pdf files must be open in binary mode to prevent end of line translation and other text mode handling on legacy systems such as Windows.
Also note that you should abort when fopen() fails and you should close the files.
Here is a modified version:
#include <errno.h>
#include <stdio.h>
#include <string.h>
#define MAXLINE 1024
int main() {
char buf2[MAXLINE];
int read_len;
FILE *fp;
FILE *fp2;
if ((fp = fopen("LearningSpark.pdf", "rb")) == NULL) {
fprintf(stderr, "Open file failed for %s: %s\n", "LearningSpark.pdf", strerror(errno));
return 1;
}
if ((fp2 = fopen("test.pdf", "wb")) == NULL) {
fprintf(stderr, "Open file failed for %s: %s\n", "test.pdf", strerror(errno));
fclose(fp);
return 1;
}
while ((read_len = fread(buf2, 1, MAXLINE, fp)) > 0) {
int write_length = fwrite(buf2, 1, read_len, fp2);
if (write_length < read_len) {
fprintf(stderr, "File write failed: %s\n", strerror(errno));
break;
}
}
fclose(fp);
fclose(fp2);
return 0;
}
I understand fopen() opens file and creates a buffer for read and write operations on that file. fopen() returns a pointer for that buffer.
So my question is, in the code below, the _copy function body has a temp matrix to transfer between the fread() and fwrite(). why cant I directly transfer from buffer to buffer?
/* example: copyfile.exe xxxxx.txt zzzzzz.txt */
#include <stdio.h>
#include <stdlib.h>
#define BUFF 8192
void _copy(FILE *source, FILE *destination);
int main(int argc, char *argv[])
{
FILE *fp1, *fp2; // fp1 source file pointer// fp2 copied file pointer
if (argc !=3 ) //command line must have 3 arguments
{
fprintf(stderr, "Usage: %s (source file) (copy file)\n", argv[0][0]);
exit(EXIT_FAILURE);
}
if ((fp1 = fopen(argv[1], "rb")) == NULL) //Opening source file
{
fprintf(stderr, "Could not open %s\n",argv[1]);
exit(EXIT_FAILURE);
}
if((fp2 = fopen(argv[2], "ab+")) == NULL) //Opening destination file
{
fprintf(stderr, "could not create %s \n",argv[2]);
exit(EXIT_FAILURE);
}
if( setvbuf(fp1,NULL, _IOFBF, BUFF) != 0) //Setting buffer for source file
{
fputs("Can't create output buffer\n", stderr);
exit(EXIT_FAILURE);
}
if( setvbuf(fp2,NULL, _IOFBF, BUFF) != 0) //Setting buffer for destination file
{
fputs("Can't create input buffer\n", stderr);
exit(EXIT_FAILURE);
}
_copy(fp1, fp2);
if (ferror(fp1)!=0)
fprintf(stderr, "Error reading file %s\n", argv[1]);
if(ferror(fp2)!=0)
fprintf(stderr, "Error writing file %s\n",argv[2]);
printf("Done coping %s (source) to %s (destination) \n",argv[1], argv[2]);
fclose(fp1);
fclose(fp2);
return (0);
}
void _copy(FILE *source, FILE *destination)
{
size_t bytes;
static char temp[BUFF];
while((bytes = fread(temp,sizeof(char),BUFF,source))>0)
fwrite(temp,sizeof(char),bytes,destination);
}
You cannot use the underlying buffer from a FILE * in another FILE *. As you were told in comment, FILE * is an opaque pointer. But you can avoid the overhead of copying data between buffers by forcing both files in non buffered mode:
setbuf(fp, NULL); // cause the stream to be unbuffered
My program needs to be able to read a .bmp image, write some data in it and then create another .bmp image. For a start, I began coding some stuff to read an image and the rewrite the same image in another file, but I got some problems in it.
Here's my code:
bmp.h:
#ifndef _BMP_H_
#define _BMP_H_
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <stdint.h>
#pragma pack(push, 1)
typedef struct {
uint16_t type;
uint32_t fileSize;
uint16_t reserved1;
uint16_t reserved2;
uint32_t offset;
} BMP_File_Header;
typedef struct {
BMP_File_Header fileHeader;
uint32_t bSize;
int32_t width;
int32_t height;
uint16_t planes;
uint16_t bitCount;
uint32_t compression;
uint32_t imageSize;
int32_t xPixelsPerMeter;
int32_t yPixelsPerMeter;
uint32_t colorUsed;
uint32_t importantColor;
} BMP_Info_Header;
#pragma pack(pop)
BMP_Info_Header* loadBMP(const char *filename, char *data);
void writeBMP(BMP_Info_Header *infoHeader, char *data);
#endif
bmp.c:
#include "bmp.h"
BMP_Info_Header* loadBMP(const char *file_name, char *data) {
FILE *file;
BMP_Info_Header *infoHeader;
int n;
//Open the file
file = fopen(file_name, "rb");
if (!file) {
fprintf(stderr, "Failed to read file %s.\n", file_name);
exit(1);
}
//Alloc and read the headers
infoHeader = (BMP_Info_Header *) malloc (sizeof(BMP_Info_Header));
n = fread(infoHeader, sizeof(BMP_Info_Header), 1, file);
//Check format
if (infoHeader->fileHeader.type != 0x4D42) {
fclose(file);
fprintf(stderr, "Invalid image");
exit(1);
}
//-------------------------Checking the image--------------------------
if (infoHeader->bSize != 40) {
fclose(file);
fprintf(stderr, "Couldn't load image correctly");
exit(1);
}
if ((infoHeader->planes != 1) || (infoHeader->bitCount != 24)) {
fclose(file);
fprintf(stderr, "Invalid image");
exit(1);
}
if (infoHeader->compression != 0) {
fclose(file);
fprintf(stderr, "This software currently does not support compressed BMP files.\n");
exit(1);
}
//Move the file through the offset until the beginning of the data itself
fseek(file, sizeof(char) * infoHeader->fileHeader.offset, SEEK_SET);
//Allocate the char array to the needed size to hold the data
data = (char *) malloc (sizeof(char) * infoHeader->imageSize);
//Actually read the image data
fread(data, sizeof(char), infoHeader->imageSize, file);
printf("%s", data);
//Verify the data
if (!data) {
fclose(file);
fprintf(stderr, "Couldn't load image data correctly\n");
exit(1);
}
fclose(file);
return infoHeader;
}
void writeBMP(BMP_Info_Header *header, char *data){
FILE *fp;
fp = fopen("output.bmp", "wb");
int n;
if (!fp) {
fclose(fp);
fprintf(stderr, "Unable to create output file\n");
exit(1);
}
//-----------------------WRITE THE IMAGE------------------------
//Header
n = fwrite(header, sizeof(char), sizeof(BMP_Info_Header), fp);
if (n < 1) {
fclose(fp);
fprintf(stderr, "Couldn't write the image header.\n");
exit(1);
}
//Offset
fseek(fp, sizeof(char) * header->fileHeader.offset, SEEK_SET);
//Data
n = fwrite(data, sizeof(char), header->imageSize, fp);
if (n < 1) {
fclose(fp);
fprintf(stderr, "Couldn't write the image data.\n");
exit(1);
}
fclose(fp);
fprintf(stdout, "Image written successfully!\n");
}
It seems to always fall off at the "Couldn't write the image data" error. Can somebody help me? I'm fairly new to programming and couldn't fix it by myself.
So, I did found the solution to my problem. Trouble is, when a BMP file is NOT compressed, the value in the imageSize variable found in the BMP_Info_Header struct is equal to 0, so when I called the fread and fwrite functions, they wouldn't read or write anything because the header->imageSize value was 0. The solution I found was to substitute the header->imageSize calls for 3 * header->width * header->height, that way I can get the real image resolution and I multiply it by 3 so I can get the exact pixel values. By the way, thanks for the help guys! Appreciate it!
Probably the problem is with the fseek, for which you don't check the return value.
fwrite(header, sizeof(char), sizeof(BMP_Info_Header), fp);
fseek(fp, sizeof(char) * header->fileHeader.offset, SEEK_SET);
fwrite(data, sizeof(char), header->imageSize, fp);
See, if your current position after writing the header is less than the fileHeader.offset, , what do you expect to happen? Fill the remaining bytes with random values ? with zero? In any case, you should explicitly write those bytes.
I created a File of 4000 blocks with a blocksize of 4096 Bytes. I wrote to some specific blocks in this file and now I want to read these blocks and write the result into a new output file.
Therefore I open the file I created in "rb" mode (storeFile) and the outputfile in "wb" mode (outputFile) as follows:
FILE * outputFile=fopen(outputFilename,"wb");
FILE * storeFile=fopen(storeFilename, "rb");
now I am trying to seek for the right position and read all blocks to the new file (outputfile):
for(i=0; i<BlocksUsed; i++)
{
fseek(storeFile,blocksToRead[i]*4096,SEEK_SET);
fread(ptr, 4096,1,storeFile);
fwrite(ptr,4096,1outputFile);
...
rewind(storeFile)
}
unfortunately this code leads to a file which is not the file I wrote to the storeFile.
The files' size is BlockUsed*4096Bytes.
What am I doing wrong?
Thank you in advance!
fseek(storeFile,blocksToRead[i]*4096,SEEK_SET);
int n = fread(ptr, 4096,1,storeFile);
if (n <0)
{printf("read error\n");return -1;}
if(fwrite(ptr,n,1outputFile) != n)
{printf("write error\n"); return -1;}
...
//rewind(storeFile)// no need . since you use fseek
Here's a silly example, but it might help illustrate a few points:
#include <stdio.h>
int
main (int argc, char *argv[])
{
FILE *fp_in, *fp_out;
int c;
/* Check command-line */
if (argc != 3) {
printf ("EXAMPLE USAGE: ./mycp <INFILEE> <OUTFILE>\n");
return 1;
}
/* Open files */
if (!(fp_in = fopen(argv[1], "rb"))) {
perror("input file open error");
return 1;
}
if (!(fp_out = fopen(argv[2], "wb"))) {
perror("output file open error");
return 1;
}
/* Copy bytewise */
while ((c = fgetc(fp_in)) != EOF)
fputc (c, fp_out);
/* Close files */
fclose (fp_out);
fclose (fp_in);
return 0;
}