-1

So all it gives me is the hex editor print out, the first 4 bytes is the integer stating how many dates, and then it is followed by chars MMDDYY so 6 chars per date. How would I write a program that reads a .bin file takes the first 4 bytes (int) as the number of elements, and then reads in each dates, parsing out the dates with the year 14? enter image description here

Something Like this?

int void main(void) {
    FILE* infile = fopen("dates.bin", "rb");
    int numOfDates;
    fgets(numOfDates, sizeof(int), infile);

    int i, totalDates;
    char allDates[numOfDates * 6];
    for(i = 0; i <= numOfDates; i++) {
        char tempDate[6];
        fgets(tempDate, sizeof(tempDate), infile);
        if(tempDate[5] == '1' && tempDate[6] == '4') {
            strcpy(allDates[totalDates * 6], tempDate);
            totalDates++; 
        }
    }
    fclose(infile);

    int j;
    FILE* outfile = fopen("datesNew.bin", "wb");
    fprintf(outfile, "%d", totalDates);
    fprintf(outfile, "%s", allDates);
    fclose(outfile);
}
Jacob
  • 113
  • 9

1 Answers1

1

The task you have is one that quite literally has to be taken step-by-step validating each input and output along the way. Only then can you have confidence in your input and output. One thing to note, and I suspect this is intentional on the part of your lesson, you have more dates in your input file than the number contained as the first integer in the file. (Number is 7, but file actually contains 8 dates)

Generally, but not always, you can structure your reading and writing as separate operations. Here, since you have to recreate your output file in the same format as the input file (with the number of 2014 dates contained in the output as the first integer value written to the file), you almost have to do it that way to efficiently make a single pass through your input file. (you can always read your input multiple times, but that is what you want to avoid from an efficiency standpoint -- disk I/O being the primary bottleneck)

As noted in my comment main() is type int, it takes arguments, use them to pass the filenames to your code. Being type int, main() therefore returns a value.

Your first task is to open your input file and read the number of dates you are expected to read from the file. That can begin with a simple:

#include <stdio.h>
#include <string.h>
#include <stdint.h>

#define DTSZ 6

int main (int argc, char **argv) {

    FILE *fp = NULL;    /* file pointer to use for both read/write */
    int32_t n = 0;      /* number of dates to read from input */

    if (argc < 3) { /* validate input/output filenames arguments given */
        fprintf (stderr, "error: insufficient input.\n"
                        "usage: %s infile.bin outfile.bin\n", argv[0]);
        return 1;
    }

    if (!(fp = fopen (argv[1], "rb"))) {  /* input file open for reading */
        fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
        return 1;
    }

    if (fread (&n, sizeof n, 1, fp) != 1) { /* read number of dates */
        fprintf (stderr, "error: read of n-dates failed.\n");
        return 1;
    }

Since you now know the number of dates you will read, you can use a variable length array (a VLA) to hold the dates. Thinking forward, you will also need to track which dates are 2014 dates so creating a check chk array of length n and initializing to all zeros will allow you to increment the index in chk that corresponds to a 2014 so you have a mechanism to use to write the 2014 date out to your output file once you have determined how many there are. So after you create your VLAs, you can loop over the number of dates in your input storing them in an array, closing the file when input in done, e.g.

    char buf[n][DTSZ + 1];          /* +1 to allow use as string */
    int32_t chk[n], n14 = 0;        /* array for 2014 indexes, number */
    memset (chk, 0, sizeof chk);    /* zero the chk array */

    for (int i = 0; i < n; i++) {   /* read/validate each date */
        if (fread (buf[i], 1, DTSZ, fp) != DTSZ) {
            fprintf (stderr, "error: failed to read date '%d'.\n", i);
            return 1;
        }
        buf[i][DTSZ] = 0;           /* nul-terminate for string use */

        printf ("read  : %s\n", buf[i]);    /* output date found */
        if (buf[i][4] == '1' && buf[i][5] == '4')   /* 2014 ? */
            n14++, chk[i]++;        /* increment count, index */
    }
    fclose (fp);                    /* close input file */

Now, knowing the number of 2014 dates and the index for those dates within your date buffer, you can open your output file, loop over the indexes in chk outputting the dates in buf that correspond to the set indexes in chk, e.g

    if (!(fp = fopen (argv[2], "wb"))) {    /* output file open */
        fprintf (stderr, "error: file open failed '%s'.\n", argv[2]);
        return 1;
    }

    if (fwrite (&n14, sizeof n14, 1, fp) != 1) {    /* output n 2014 */
        fprintf (stderr, "error: write of n14-dates failed.\n");
        return 1;
    }

    for (int i = 0; i < n; i++)     /* for each date read */
        if (chk[i]) {               /* check 2014 index  */
            printf ("write : '%s' to output.\n", buf[i]);
            if (fwrite (buf[i], 1, DTSZ, fp) != DTSZ) { /* write/validate */
                fprintf (stderr, "error: write of '%s' failed.\n", buf[i]);
                return 1;
            }
        }

    if (fclose (fp) == EOF) {    /* close output file */
        /* handle error */
    }

    return 0;
}

note: you should also check the return of fclose after any write operation. fclose returns 0 on success or EOF on error and sets errno to contain specifics about the error encountered. A check of if (fclose (fp) == EOF) {/*... handle error ...*/} is sufficient. The concern are that a prior write operation may have encountered an error that is not reported until the stream is closed, the close itself was interrupted, or an IO error occurred. Also note that fclose does not cause data to be immediately written to disk in all cases (the kernel may buffer output, etc..). To force an immediate write disk, a call fsync to insure the buffer is flushed and data is written.

Putting those pieces together, you could expect the following:

Example Use/Output

$ ./bin/freaddates dat/bindates.bin dat/newdates.bin
read  : 050514
read  : 032313
read  : 012514
read  : 081612
read  : 073114
read  : 020612
read  : 112315
write : '050514' to output.
write : '012514' to output.
write : '073114' to output.

Verify Written File

$ hexdump -C dat/newdates.bin
00000000  03 00 00 00 30 35 30 35  31 34 30 31 32 35 31 34  |....050514012514|
00000010  30 37 33 31 31 34                                 |073114|
00000016

Look things over and let me know if you have any further questions.

David C. Rankin
  • 69,681
  • 6
  • 44
  • 72