Получение времени файла с помощью stat & tm structor в Linux - PullRequest
0 голосов
/ 27 апреля 2018

Я использовал g ++ на linux с eclipse. Я делаю код, который получает время файла и выходной файл месяц, час и т. Д ...

Во время отладки значение time1 неожиданно изменилось, но я понятия не имею об этой проблеме.

В чем проблема этого кода?

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>


struct stat stat1, stat2;
struct tm *time1, *time2;

void filestat1(void);
void filestat2(void);
void filetime1(void);
void filetime2(void);
void datecmp(void);
void timecmp(void);

int main(void)
{
    filestat1();
    filestat2();
    filetime1();
    filetime2();
    datecmp();
    timecmp();
}

void filestat1(void)
{
    // check if there is no text1
    int check = 0;
    check = stat("text1", &stat1);    

    if(check != 0)
    {
        printf("Error : there is no text1\n");
    }
    return;
}


void filestat2(void)
{
    // check if there is no text2
    int check = 0;
    check = stat("text2", &stat2);    

    if(check != 0)
    {
        printf("Error : there is no text2\n");
    }
    return;
}


void filetime1(void)
{
    time1 = localtime(&stat1.st_mtime); //!!! this change unexpectedly
    return;
}


void filetime2(void)
{
    time2 = localtime(&stat2.st_mtime);    

    return;
}    

void datecmp(void)
{
    printf("date compare\n");
    // compare tm_mon
    if(time1->tm_mon > time2->tm_mon)
        printf("time1 is early \n");
    else if(time1->tm_mon < time2->tm_mon)
        printf("time2 is early \n");
    else{
        // compare tm_mday
        if(time1->tm_mday > time2->tm_mday)
            printf("time1 is early \n");
        else if(time1->tm_mday < time2->tm_mday)
            printf("time2 is early \n");
        // same date
        else
            printf("same time \n");
        }
        printf("\n");
}


void timecmp(void)
{

printf(time1->tm_hour);
printf(time2->tm_hour);
printf("time compare\n");
// compare hour
if(time1->tm_hour > time2->tm_hour)
    printf("time1 is early \n");
else if(time1->tm_hour < time2->tm_hour)
    printf("time2 is early \n");
else{
    // compare minutes
    if(time1->tm_min > time2->tm_min)
        printf("time1 is early \n");
    else if(time1->tm_min < time2->tm_min)
        printf("time2 is early \n");
    // same time
    else
        printf("same time \n");

    }
}

Ответы [ 2 ]

0 голосов
/ 27 апреля 2018

Вы используете глобальные переменные, совершенно без необходимости, делая вашу жизнь сложнее, чем она должна быть. Нам, людям, очень трудно отследить, где изменяются глобальные переменные, особенно когда у вас есть несколько глобальных переменных с очень похожими именами.

Итак, вместо того, чтобы пытаться разгадать все это, давайте перепишем его, используя параметры функции, без каких-либо глобальных переменных.

Сначала мы сообщаем библиотеке C, что нам нужны функции POSIX.1-2008, и включаем заголовки, которые предоставляют нам необходимую функциональность:

#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <limits.h>
#include <time.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>

Далее, давайте определим функцию, которая принимает имя файла в качестве параметра и указывает, где функция может хранить метки времени последнего доступа и последней модификации. Если функция успешна, она вернет 0; в противном случае он вернет -1 с errno, установленным для указания ошибки.

int filetime(const char *path,
             time_t *accessed, long *accessed_nsec,
             time_t *modified, long *modified_nsec)
{
    struct stat  info;

    /* Path must not be NULL or empty. */
    if (!path || !path[0]) {
        errno = EINVAL;
        return -1;
    }

    /* Get file statistics. */
    if (stat(path, &info) == -1)
        return -1; /* errno was set by stat() */

    /* Save timestamps. */
    if (accessed)
        *accessed = info.st_atim.tv_sec;
    if (accessed_nsec)
        *accessed_nsec = info.st_atim.tv_nsec;
    if (modified)
        *modified = info.st_mtim.tv_sec;
    if (modified_nsec)
        *modified_nsec = info.st_mtim.tv_nsec;

    /* Success. */
    return 0;
}

Давайте продолжим, написав простой main(), который принимает одно или несколько имен файлов в качестве параметров командной строки и описывает их. Мне нравится начинать с основного, проверяя количество аргументов командной строки и, если указано, первый аргумент. Если нет, или первым является -h или --help, я хотел бы напечатать использование утилиты. Таким образом, я могу хранить свои примеры программ в своих собственных каталогах, и чтобы найти нужную, я могу просто выполнить каждую без параметров, чтобы увидеть, что делает каждая из них. Это намного быстрее, чем читать источники!

int main(int argc, char *argv[])
{
    int arg;

    if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
        fprintf(stderr, "\n");
        fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
        fprintf(stderr, "       %s FILENAME ...\n", argv[0]);
        fprintf(stderr, "\n");
        fprintf(stderr, "This program will print the last access and\n");
        fprintf(stderr, "last modification timestamps for each of\n");
        fprintf(stderr, "the specified files.\n");
        fprintf(stderr, "\n");
        return EXIT_FAILURE;
    }

Поскольку существует хотя бы один, но, возможно, несколько параметров имени файла, мы обрабатываем каждый из них в цикле:

    for (arg = 1; arg < argc; arg++) {
        time_t     accessed, modified;
        long       accessed_ns, modified_ns;
        struct tm  accessed_localtime, modified_localtime;

В цикле мы сначала вызываем нашу функцию filetime(). Обратите внимание, как мы объявили переменные, которые мы хотим заполнить выше, и как мы вызываем функцию: &accessed возвращает указатель на accessed.

        if (filetime(argv[arg], &accessed, &accessed_ns,
                                &modified, &modified_ns)) {
            /* Nonzero return value, so an error occurred! */
            fprintf(stderr, "%s: %s.\n", argv[arg], strerror(errno));
            return EXIT_FAILURE;
        }

В C параметры функции передаются по значению, а не по ссылке. Это означает, что если параметр функции скажет int foo, любые изменения в foo внутри функции видны только внутри функции; изменения не видны звонящему. Когда мы передаем указатель на переменную, скажем, int *foo, изменения в foo все еще видны только внутри функции, но *foo относится к значению, на которое указывает указатель; и изменяется на , чтобы были видимы для вызывающей стороны

Короче говоря, когда мы хотим, чтобы функция могла изменять значение переменной, мы используем указатель. Вот как работает С.

Теперь, когда у нас есть время в эпохе Unix (time_t), мы хотим разделить их на поля местного времени:

        if (!localtime_r(&accessed, &accessed_localtime) ||
            !localtime_r(&modified, &modified_localtime)) {
            fprintf(stderr, "%s: Cannot compute timestamps in local time: %s.\n", argv[arg], strerror(errno));
            return EXIT_FAILURE;
        }

Обратите внимание, что я снова использовал функцию POSIX.1-2008, localtime_r(). В учебных пособиях вы часто видите, что вместо этого используется старый localtime(), но он может использовать глобальную переменную внутри себя (он всегда может возвращать указатель на одну и ту же структуру, повторно используя ее для каждого вызова); localtime_r() лучше.

Видите, как второй параметр localtime_r также является указателем (struct tm)? Опять же, именно так вы выполняете функции, которые изменяют некоторые значения таким образом, чтобы это было видно для вызывающей стороны.

Кроме того, localtime_r() (или localtime()) редко дают сбой, поэтому многие просто игнорируют проверку его на наличие ошибок. Этому нет оправдания, так как всего на несколько строк больше кода, и если в какой-то момент произойдет ошибка, пользователь будет гораздо более доволен четким кодом ошибки, а не просто увидит сбой программы из-за сегментации неисправность.

Осталось только распечатать собранную информацию. Мне нравится использовать вариант международного стандарта ISO 8601 для формата времени; в частности, он сортирует в нужном порядке времени, даже если сортируется по алфавиту. (Мой вариант заключается в том, что мне нравится использовать пробел, а не T между датой и временем.)

        printf("%s:\n", argv[arg]); /* The file name or path */

        printf("    Modified: %04d-%02d-%02d %02d:%02d:%02d.%03d\n",
               modified_localtime.tm_year + 1900,
               modified_localtime.tm_mon + 1,
               modified_localtime.tm_mday,
               modified_localtime.tm_hour,
               modified_localtime.tm_min,
               modified_localtime.tm_sec,
               modified_ns / 1000000L);

        printf("    Accessed: %04d-%02d-%02d %02d:%02d:%02d.%03d\n",
               accessed_localtime.tm_year + 1900,
               accessed_localtime.tm_mon + 1,
               accessed_localtime.tm_mday,
               accessed_localtime.tm_hour,
               accessed_localtime.tm_min,
               accessed_localtime.tm_sec,
               accessed_ns / 1000000L);

        /* Make sure everything written to stdout
           is actually written to standard output right now. */
        fflush(stdout);
    }

    return EXIT_SUCCESS;
}

fflush(stdout) сообщает библиотеке C, что все предыдущие записи в stdout должны быть действительно записаны в стандартный вывод. (По умолчанию, stdout буферизируется, а stderr - не буферизируется.) Обычно библиотека C сбрасывает вывод на каждой новой строке, но наличие явного сброса также напоминает нам программистам-людям, что мы хотим, чтобы все до сих пор печаталось, на самом деле появиться на выходе программы стандартным в этой точке. (Таким образом, если один из файлов находится в какой-либо медленной файловой системе, например, на старой USB-карте или в сетевом ресурсе, информация о предыдущих файлах будет отображена до того, как программа получит доступ к медленному файлу. По сути, «остановка» произойдет при ожидаемом место для пользователей.)

Вероятно, стоит упомянуть relatime и другие связанные параметры монтирования на этом этапе. Проще говоря, это означает, что во избежание количества операций записи на носитель из-за доступа для чтения время доступа не всегда обновляется. Таким образом, если вы не видите, что он меняется даже после чтения файла (например, с помощью cat FILENAME >/dev/null), это просто означает, что в вашей системе включены параметры монтирования, которые сокращают время доступа к обновлениям, чтобы ускорить доступ к вашей файловой системе и уменьшить количество пишет в него. Это хороший вариант; Я использую это.

Наконец, большинство файловых систем Linux вообще не имеют созданной метки времени. Поля st_ctimest_ctim.tv_sec и st_ctim.tv_nsec) относятся к последнее изменение статуса отметка времени. Он отслеживает изменения владельца, группы, прав доступа и количества жестких ссылок.

Когда вы изучаете приведенный выше код, особенно предложения if, полезно помнить, что в C логическая операция ИЛИ, ||, имеет короткое замыкание: сначала проверяется левая сторона, но если она не получается, правая сторона вообще не оценивается. Итак, если у вас есть, например, int x = 1, y = 0; и вы делаете (x == 0 || ++y), y не будет увеличиваться вообще. Я использую это при рассмотрении argv[1] в самом первом предложении if в main().

0 голосов
/ 27 апреля 2018

localtime возвращает указатель на статическую структуру. Вам необходимо скопировать результат перед повторным вызовом localtime.

Я бы объявил time1 и time2 как структуры вместо указателей для хранения значений.

struct tm time1, time2;

void filetime1(void)
{
    struct tm *tmp = localtime(&stat1.st_mtime);
    if (tmp == NULL) {
        //... handle error
    }
    time1 = *tmp;
}

Аналогично для filetime2.

Если вы пишете многопоточный код, безопаснее использовать реентерабельный вариант функции, localtime_r. В этом случае вы передаете указатель на структуру результата.

void filetime1(void)
{
    struct tm *tmp = localtime_r(&stat1.st_mtime, &time1);
    if (tmp == NULL) {
        //... handle error
    } else {
        assert(tmp == &time1);
    }
}
...