0

I am working on a photo extraction program for a course I am taking.

For this program, argv[1] is the name of the file from which I want to extract images from. jpg_name relates to the name of each image I am extracting; I am attempting to name each jpg. numerically starting from 1. As there are 50 photos to be extracted, I would like to stop the program after all 50 images have been extracted. Unfortunately, my program is not sure when to terminate, and as such I believe the last photo is being overwritten multiple times, and I am unsure how to fix this. However, photos 1-49 turn out fine, it is just photo 50 I am having issue with.

Things I have tried to far include implementing an if condition involving fread where if the program is unable to read 512 bytes of code into the array, it would terminate. Unfortunately, this does not seem to work either. Any suggestions would be appreciated:

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        printf("Invalid entry.\n");
        return 0;
    }

    int counter = 1;
    FILE* images;
    char jpg_name[8];

    // Check if bytes are jpg. signatures.
    for (int n = 0; counter < 51; n = n + 512)
    {
        // Open file for reading.
        FILE *file = fopen(argv[1], "r");
        if (!file)
        {
            return 1;
        }

        unsigned char array[512];
        fseek(file, n, SEEK_SET);
        fread(array, 1, 512, file); // if EOF, won't have 512 to write into!!!
        if (fread(array, 1, 512, file) != 512)
        {
            return 2;
        }

        if (array[0] == 0xff && array[1] == 0xd8 && array[2] == 0xff && (array[3] & 0xf0) == 0xe0)
        {
            // Convert integer to string and store into jpg character array. Increment image number.
            sprintf(jpg_name, "%03i.jpg", counter);
            counter++;

            // Open images file to write into, allocate memory to jpg file to write into, write 512 bytes from array into image file.
            images = fopen(jpg_name, "a");
            fwrite(array, 1, 512, images);
            fclose(images);
        }
        else // If 1st 4 bytes aren't jpg signature.
        {
            if (counter > 1)
            {
                images = fopen(jpg_name, "a");
                fwrite(array, 1, 512, images);
                fclose(images);
            }
        }
        fclose(file);
    }
}
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
ckwan
  • 7
  • 1
  • Is the course CS50? It has a question asking about image recovery in one of the psets. – Jonathan Leffler Jul 14 '20 at 02:36
  • 2
    Your code contains: `fread(array, 1, 512, file); if (fread(array, 1, 512, file) != 512)` which functionally throws away the first block of data — it reads it and then immediately reads a new block of data over the first block. This might be part or all of your problem. You should probably also check the return value from the first `fread()`. At least, that would normally be a good choice. – Jonathan Leffler Jul 14 '20 at 02:39
  • @JonathanLeffler Yes it is CS50! So I just added that line of code to see if it would help in terminating the program when I get to the last image; removing ```if (fread(array, 1, 512, file) != 512)``` or adding it doesn't seem to make a difference with regards to 49/50 images working and the last one not working. – ckwan Jul 14 '20 at 02:51
  • @ckwan You didn't seem to understand Jonathan's comment. This wasn't about adding code. The naked (i.e. unchecked) `fread` call shouldn't be there. Further, from everything I see in this code, the `fseek` call is pointless.From all appearances you shoved that in to avoid the final-duplicate read that was triggering the `return 2;` Get rid of the `fseek`, get rid of the naked `fread` call, and if necessary, retain the result of the tested `fread` for debugger examination rather than just testing against 512. – WhozCraig Jul 14 '20 at 03:39
  • 2
    You should only open the file once, then read 50 times. After each read the file position will advance, so no seeks are necessary. – William Pursell Jul 14 '20 at 04:17
  • Appears related to [cs50 pset4 Recover - why does writing entire file to memory fail check50?](https://stackoverflow.com/q/62605698/3422102) – David C. Rankin Jul 14 '20 at 04:25
  • 1
    `counter < 51` You should not care about the number of images found. If the file is read till the end before you found50 images, what's the point of entering the loop again? – Gerhardh Jul 14 '20 at 05:50
  • @All Thanks everyone for the clarifications and pointing out the redundancies/errors in my code. After reading through all the feedback, I realized my first ```fread()``` was redundant and as such I removed it. I also removed the ```for``` loop as well as the use of the ```fseek``` function. In place, I substituted a while loop to just run through the file until it runs out of 512 blocks to read. Finally, I moved the opening of the file above the ```while``` loop, such that I only have to open the file once. Compiling and running it yielded all 50 images. :) – ckwan Jul 14 '20 at 18:43
  • Does this answer your question? [Exiting out of file with feof function](https://stackoverflow.com/questions/62886327/exiting-out-of-file-with-feof-function) – user3629249 Jul 15 '20 at 05:21

2 Answers2

1

You seem to have a basic misunderstanding about functions and return calls. I suspect you think: an fread instruction reads data, and an if instruction checks if something is true. So you read the data, then check how much was read. However, you are missing the fact that if executes the instructions inside the () in order to see whether the result is true. So if(fread(array, 1, 512, file) != 512) reads data and checks the result.

If you want to read data and check the result, use if(fread(array, 1, 512, file) != 512). If you want to read data and not check the result, use fread(array, 1, 512, file);. Don't use both.

You could also choose to use a variable, for example:

int number_of_bytes_read = fread(array, 1, 512, file);
if(number_of_bytes_read != 512) // this doesn't read data, it just gets the number from the variable
{
    ...
user253751
  • 57,427
  • 7
  • 48
  • 90
  • Understood. Thank you clearing that up; that was indeed what I was under the impression of. – ckwan Jul 14 '20 at 18:12
0

If I understand correctly, you are trying to extract images from one jpeg file and splitting it into multiple files. Here are a few questions to think about

  1. fseek, why would you need that unless you are trying to go to the end of file for length and comeback to the current offset. Otherwise, I advice you to remove it.
  2. Two freads are not required. Also, feof() should tell you if you have reached eof if fread gives anything other than what you are seeking for.

I have slightly edited your code, see if that is what you are looking for


    #include <stdio.h>
    #include <stdlib.h>
    #include <stdbool.h>
    #include <string.h>
    
    int main(int argc, char *argv[])
    {
        if (argc != 2)
        {
            printf("Invalid entry.\n");
            return 0;
        }
    
        int counter = 1;
        FILE* images;
        char jpg_name[8];
        int num = 0;
        int total_num = 0;
    
        // Check if bytes are jpg. signatures.
        for (int counter = 0; counter < 51; counter ++)
        {
            // Open file for reading.
            FILE *file = fopen(argv[1], "r");
            if (!file)
            {
                printf("Exiting at 1\n");
                return 1;
            }
    
            unsigned char array[512];
            // don't understand why you need this ? unless, you are trying to go to end of file and get back to the current
            // offset
            fseek(file, 0, SEEK_SET);
            total_num = 0;
            while((num = fread(array, 1, 512, file)) == 512)
            {
                if (array[0] == 0xff && array[1] == 0xd8 && array[2] == 0xff && (array[3] & 0xf0) == 0xe0)
                {
                    // Convert integer to string and store into jpg character array. Increment image number.
                    sprintf(jpg_name, "%03i.jpg", counter);
                    counter++;
    
                    // Open images file to write into, allocate memory to jpg file to write into, write 512 bytes from array into image file.
                    images = fopen(jpg_name, "a");
                    fwrite(array, 1, 512, images);
                    fclose(images);
                }
                else // If 1st 4 bytes aren't jpg signature.
                {
                    if (counter > 1)
                    {
                        images = fopen(jpg_name, "a");
                        fwrite(array, 1, 512, images);
                        fclose(images);
                    }
                }
            }
            fclose(file);
        }
        printf("Exiting here\n");
    }