简体   繁体   中英

Sort an array of struct tm

I am currently creating a personal diary in C and I'd like to have the possibility to print the post sorted by date. I can extract the date using the struct tm but I don't know to sort the dates so that the most recent is on top. This is my whole function here :

void dateOrder() {
    FILE *postfile = fopen("post.txt", "r");

    int numofpost = getNumOfPost(postfile);
    int dates[numofpost];

    struct tm ptime;

    char *elt = malloc(5 * sizeof(char));
    char *dref = "Date";
    char *href = "Heure";
    char c = 'c';

    char *pseudo = malloc(20 * sizeof(char));
    int pseudolen = 0;

    rewind(postfile);

    while (!feof(postfile)) {

        fscanf(postfile, "%s", elt);

        if (strcmp(elt, dref) == 0) {
            fseek(postfile, 3, SEEK_CUR);
            fscanf(postfile, "%d/%d/%d", (int)&(ptime.tm_mday), (int)&(ptime.tm_mon), (int)&(ptime.tm_year));
        }

        if (strcmp(elt, href) == 0) {
            fseek(postfile, 3, SEEK_CUR);
            fscanf(postfile, "%d:%d", (int)&(ptime.tm_hour), (int)&(ptime.tm_min));
        }

        ptime.tm_year -= 1900;
        ptime.tm_mon -= 1;
        ptime.tm_sec = 0;
        ptime.tm_isdst = -1;
        int rep = mktime(&ptime);

        if (rep != -1) {
            dates[i++] = rep;
        }
    }

    insertsort(dates, sizeof(dates)/sizeof(dates[0]));

    for (int i = 0; i < numofpost; i++) {
        c = 'c';
        rewind(postfile);

        while (!feof(postfile) && c != 24) {

            fscanf(postfile, "%s", elt);

            if (strcmp(elt, "Pseudo") == 0) {
                fseek(postfile, 3, SEEK_CUR);
                fscanf(postfile, "%s", pseudo);
                pseudolen = strlen(pseudo);
            }

            if (strcmp(elt, dref) == 0) {

                fseek(postfile, 3, SEEK_CUR);
                fscanf(postfile, "%d/%d/%d", (int)&(ptime.tm_mday), (int)&(ptime.tm_mon), (int)&(ptime.tm_year));
            }

            if (strcmp(elt, href) == 0) {
                fseek(postfile, 3, SEEK_CUR);
                fscanf(postfile, "%d:%d", (int)&(ptime.tm_hour), (int)&(ptime.tm_min));
            }

            ptime.tm_year -= 1900;
            ptime.tm_mon -= 1;
            ptime.tm_sec = 0;
            ptime.tm_isdst = -1;
            int mkt = mktime(&ptime);

            if (mkt == dates[i]) {
                fseek(postfile, -39, SEEK_CUR);
                fseek(postfile, -pseudolen, SEEK_CUR);

                while (c != 24) {
                    c = fgetc(postfile);

                    if (c == 24)
                        continue;

                    printf("%c", c);
                }
            }
        }
    }

    fclose(postfile);   
}

And this is struct tm :

struct tm {
   int tm_sec;         /* seconds,  range 0 to 59          */
   int tm_min;         /* minutes, range 0 to 59           */
   int tm_hour;        /* hours, range 0 to 23             */
   int tm_mday;        /* day of the month, range 1 to 31  */
   int tm_mon;         /* month, range 0 to 11             */
   int tm_year;        /* The number of years since 1900   */
   int tm_wday;        /* day of the week, range 0 to 6    */
   int tm_yday;        /* day in the year, range 0 to 365  */
   int tm_isdst;       /* daylight saving time             */
 };

You can write a comparison function using mktime() and difftime() , and then use qsort() to sort an array of tm structs. The comparison function cmp_dates_descend() below can be used by qsort() to sort an array of dates in descending order:

#include <stdlib.h>
#include <time.h>

#define NUM_DATES  10  /* for example */

int cmp_dates_descend(const void *d1, const void *d2);

int main(void)
{
    /* ... */

    struct tm arr_dates[NUM_DATES];

    /* ... */

    size_t num_dates = sizeof arr_dates / sizeof *arr_dates;

    qsort(arr_dates, num_dates, sizeof *arr_dates, cmp_dates_descend);

    /* ... */

    return 0;
}

int cmp_dates_descend(const void *d1, const void *d2)
{
    struct tm *date_1 = (struct tm *) d1;
    struct tm *date_2 = (struct tm *) d2;

    return double d = -difftime(mktime(date_1), mktime(date_2));
}

Note that this method may encounter problems for large date differences. Since difftime() returns a double (representing a time difference in seconds), the return value may not be representable in an int , which is the value returned by comparison functions used by qsort() . Where INT_MAX == 2147483647 , typical of 4 byte int s, date differences of more than about 68 years will lead to overflow in the conversion from double to int , and thus undefined behavior. If such large date differences are to be handled, perhaps a custom sort function should be written.

Edit

@chqrlie has pointed out in the comments that this method could also lead to erroneous comparisons for extremely close dates (fractions of a second), since if difftime(mktime(date_1), mktime(date_2)) is less than 1 in magnitude, the value will be converted to 0 upon return, thus comparing as equal. To avoid this complication, the result of difftime() can be stored in a double and compared with 0 to determine a return value. This is a common trick with comparison functions; this also removes the previous issue with large date differences. Here is an improved comparison function:

int cmp_dates_descend(const void *d1, const void *d2)
{
    struct tm *date_1 = (struct tm *) d1;
    struct tm *date_2 = (struct tm *) d2;

    double d = difftime(mktime(date_1), mktime(date_2));

    return (d < 0) - (d > 0);
}

Edit 2

To leave the array unchanged, which the comparison function is supposed to do as it gets const pointers to the array elements, we can make copies of the tm structures in the comparison function and invoke mktime() on the copies at a small cost in performance:

int cmp_dates_descend(const void *d1, const void *d2)
{
    struct tm date_1 = *(const struct tm *)d1;
    struct tm date_2 = *(const struct tm *)d2;

    double d = difftime(mktime(&date_1), mktime(&date_2));

    return (d < 0) - (d > 0);
}

Thanks to the answers and comments here is how I print the posts sorted by date

int check(int i, struct tm *dates, struct tm ptime){
   if(dates[i].tm_year == ptime.tm_year && dates[i].tm_mon == ptime.tm_mon && 
      dates[i].tm_mday == ptime.tm_mday && dates[i].tm_hour == ptime.tm_hour && 
      dates[i].tm_min == ptime.tm_min)

      return 1;
 return 0;
}
int compareDates(const void *d1, const void *d2){
struct tm date_1 = *(const struct tm *)d1;
struct tm date_2 = *(const struct tm *)d2;

double d = difftime(mktime(&date_1), mktime(&date_2));

return (d < 0) - (d > 0);
}

void dateOrder(){ //print the post sorted by date

FILE *postfile = fopen("post.txt", "r");

int numofpost = getNumOfPost(postfile);
struct tm dates[numofpost];
int pseudolen = 0, i = 0;

struct tm ptime;

char *elt = malloc(5*sizeof(char));
char *pseudo = malloc(20*sizeof(char));
char *dref = "Date"; //Word to find to get the date
char *href = "Heure"; //Word to find to get hour
char c = 'c';

rewind(postfile);

while(!feof(postfile)){

    fscanf(postfile, "%s", elt);

    if(strcmp(elt, dref) == 0){
        fseek(postfile, 3, SEEK_CUR);
        fscanf(postfile, "%d/%d/%d", (int)&(ptime.tm_mday), (int)&(ptime.tm_mon), (int)&(ptime.tm_year));

        dates[i].tm_year = ptime.tm_year;
        dates[i].tm_mon = ptime.tm_mon;
        dates[i].tm_mday = ptime.tm_mday;
    }

    if(strcmp(elt, href) == 0){
        fseek(postfile, 3, SEEK_CUR);
        fscanf(postfile, "%d:%d", (int)&(ptime.tm_hour), (int)&(ptime.tm_min));

        dates[i].tm_hour = ptime.tm_hour;
        dates[i++].tm_min = ptime.tm_min;
    }
}

size_t num_dates = sizeof(dates)/sizeof(*dates);
qsort(dates, num_dates, sizeof(*dates), compareDates);

for(int i = 0; i < numofpost; i++){
    c = 'c';
    rewind(postfile);

    while(!feof(postfile) && c != 24){ //We read the file until the end c is equal to 24 only if a already founded the wanted post

        fscanf(postfile, "%s", elt);

        if(strcmp(elt, "Pseudo") == 0){
            fseek(postfile, 3, SEEK_CUR);
            fscanf(postfile, "%s", pseudo);
            pseudolen = strlen(pseudo);
        }

        if(strcmp(elt, dref) == 0){
            fseek(postfile, 3, SEEK_CUR);
            fscanf(postfile, "%d/%d/%d", (time_t)&(ptime.tm_mday), (time_t)&(ptime.tm_mon), (time_t)&(ptime.tm_year));
        }

        if(strcmp(elt, href) == 0){
            fseek(postfile, 3, SEEK_CUR);
            fscanf(postfile, "%d:%d", (time_t)&(ptime.tm_hour), (time_t)&(ptime.tm_min));
        }

        if(check(i, dates, ptime)){ //check look if the member of struct are the same in dates ans ptime
            fseek(postfile, -40, SEEK_CUR);
            fseek(postfile, -pseudolen, SEEK_CUR);

            while(c != 24){ //while c != 24 is because user has stop typing a post when typing Ctrl + X == 24 in ascii code
                c = fgetc(postfile);

                if(c == 24)
                    continue;
                printf("%c", c);
            }
        }

        if(ftell(postfile)+15 < feof(postfile)) //If it is not the last post
            fseek(postfile, 15, SEEK_CUR); //I go to next post*
    }
    printf("\n\n\n\n");
}

  fclose(postfile);
  printf("\n\n");   
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM