[英]Sort an array of struct tm
我目前正在用C創建個人日記,我希望可以打印按日期排序的帖子。 我可以使用struct tm提取日期,但是我不知道如何對日期進行排序,因此最新日期排在最前面。 這是我的整個功能:
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);
}
這是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 */
};
您可以使用mktime()
和difftime()
編寫比較函數,然后使用qsort()
對tm
結構數組進行排序。 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));
}
請注意,對於大的日期差異,此方法可能會遇到問題。 由於difftime()
返回double
difftime()
值(表示以秒為單位的時間差),因此返回值可能無法以int
表示, int
是qsort()
使用的比較函數返回的值。 在INT_MAX == 2147483647
(典型值為4字節int
的情況下,日期差超過68年將導致從double
到int
的轉換溢出,從而導致不確定的行為。 如果要處理如此大的日期差異,則可能應編寫自定義排序函數。
編輯
@chqrlie在評論中指出,此方法也可能導致極接近日期(幾分之一秒)的錯誤比較,因為如果difftime(mktime(date_1), mktime(date_2))
大小小於1
,則值將在返回時轉換為0
,從而進行比較。 為了避免這種復雜性,可以將difftime()
的結果存儲在double
並與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;
double d = difftime(mktime(date_1), mktime(date_2));
return (d < 0) - (d > 0);
}
編輯2
為了使數組保持不變(比較函數在獲取指向數組元素的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);
}
多虧了這里的答案和評論,這是我如何打印按日期排序的帖子
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.