简体   繁体   English

对struct tm数组进行排序

[英]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. 我目前正在用C创建个人日记,我希望可以打印按日期排序的帖子。 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. 我可以使用struct tm提取日期,但是我不知道如何对日期进行排序,因此最新日期排在最前面。 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:

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. 您可以使用mktime()difftime()编写比较函数,然后使用qsort()tm结构数组进行排序。 The comparison function cmp_dates_descend() below can be used by qsort() to sort an array of dates in descending order: qsort()可以使用下面的比较函数cmp_dates_descend()对日期数组进行降序排序:

#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() . 由于difftime()返回double difftime()值(表示以秒为单位的时间差),因此返回值可能无法以int表示, intqsort()使用的比较函数返回的值。 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. INT_MAX == 2147483647 (典型值为4字节int的情况下,日期差超过68年将导致从doubleint的转换溢出,从而导致不确定的行为。 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. @chqrlie在评论中指出,此方法也可能导致极接近日期(几分之一秒)的错误比较,因为如果difftime(mktime(date_1), mktime(date_2))大小小于1 ,则值将在返回时转换为0 ,从而进行比较。 To avoid this complication, the result of difftime() can be stored in a double and compared with 0 to determine a return value. 为了避免这种复杂性,可以将difftime()的结果存储在double并与0比较以确定返回值。 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 编辑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: 为了使数组保持不变(比较函数在获取指向数组元素的const指针时应该执行此操作),我们可以在比较函数中复制tm结构的副本,并以mktime()性能代价在副本上调用mktime()

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");   
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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