![](/files/books/160/oblozhka-knigi-linux-programmirovanie-v-primerah-258381.jpg)
Текст книги "Linux программирование в примерах"
Автор книги: Арнольд Роббинс
Жанры:
Программирование
,сообщить о нарушении
Текущая страница: 15 (всего у книги 55 страниц)
fchown()
и fchmod()
для обеспечения безопасностиВ исходных системах Unix были только системные вызовы chown()
и chmod()
. Однако, на сильно загруженных системах эти системные вызовы попадают в условия состязания, посредством чего злоумышленник может организовать замещение другим файлом файла, у которого изменяется владелец или права доступа.
Однако, после открытия файла условие состязания больше не представляет проблему. Программа может использовать stat()
с именем файла для получения информации о файле. Если получены сведения, которые ожидались, после открытия файла fstat()
может проверить, что файл тот же самый (сравнив поля st_dev
и st_ino
структур struct stat
«до» и «после»).
Когда программа знает, что файлы те же самые, владение или права доступа могут быть изменены с помощью fchown()
или fchmod()
.
Эти системные вызовы, также как lchown()
, сравнительно недавние;[63]63
fchown()
и fchmod()
были введены в 4 2 BSD, но не включались в System V до выпуска 4 – Примеч. автора.
[Закрыть] в старых системах Unix их не было, хотя в современных совместимых с POSIX системах они есть.
Соответствующих функций futime()
или lutime()
нет. В случае futime()
это (очевидно) потому, что временные отметки не являются критическими для безопасности системы в том же отношении, что для владения и прав доступа, lutime()
отсутствует потому, что временные отметки неуместны для символических ссылок.
• Иерархия файлов и каталогов, как она видится пользователю, является одним логическим деревом, корень которого находится в /
. Оно составлено из одного или более разделов, каждый из которых содержит файловую систему. Внутри файловой системы в индексах хранятся данные о файлах (метаданные), включая размещение блоков данных.
• Каталоги осуществляют связь между именами файлов и индексами. Концептуально содержимое каталога, которое является просто последовательностью пар (индекс, имя). Каждый элемент каталога для файла называется (прямой) ссылкой, а файлы могут иметь множество ссылок. Прямые ссылки, поскольку они работают лишь по номеру индекса, все должны находиться в одной файловой системе. Символические ссылки являются указателями на файлы или каталоги и работают на основе имени файла, а не номера индекса, поэтому их использование не ограничено одной и той же файловой системой.
• Прямые ссылки создаются с помощью link()
, символические ссылки создаются с помощью symlink()
, ссылки удаляются с помощью unlink()
, а переименовываются файлы (с возможным перемещением в другой каталог) с помощью rename()
. Блоки данных файла не освобождаются до тех пор, пока счетчик ссылок не достигнет нуля и не закроется последний открытый дескриптор файла.
• Каталоги создаются с помощью mkdir()
, а удаляются с помощью rmdir()
; перед удалением каталог должен быть пустым (не оставлено ничего, кроме '.
' и '..
'). GNU/Linux версия функции ISO С remove()
вызывает соответствующие функции unlink()
или rmdir()
.
• Каталоги обрабатываются с помощью функций opendir()
, readdir()
, rewinddir()
и closedir()
. struct dirent
содержит номер индекса и имя файла. Максимально переносимый код использует в члене d_name
только имя файла. Функции BSD telldir()
и seekdir()
для сохранения и восстановления текущего положения в каталоге широко доступны, но не полностью переносимы, как другие функции работы с каталогами.
• Вспомогательные данные получаются с помощью семейства системных вызовов stat()
, структура struct stat
содержит всю информацию о файле за исключением имени файла. (В самом деле, поскольку у файла может быть множество имен или он может совсем не иметь ссылок, невозможно сделать имя доступным.)
• Макрос S_ISxxx()
в
дает возможность определить тип файла. Функции major()
и minor()
из
дают возможность расшифровки значений dev_t
, представляющих блочные и символьные устройства.
• Символические ссылки можно проверить, использовав lstat()
, а поле st_size
структуры struct stat
для символической ссылки возвращает число байтов, необходимых для размещения имени указываемого файла. Содержимое символической ссылки читают с помощью readlink()
. Нужно позаботиться о том, чтобы размер буфера был правильным и чтобы завершить полученное имя файла нулевым байтом, чтобы можно было его использовать в качестве строки С.
• Несколько разнообразных системных вызовов обновляют другие данные: семейство chown()
используется для смены владельца и группы, процедуры chmod()
для прав доступа к файлу, a utime()
для изменения значений времени доступа и изменения файла.
1. Напишите программу 'const char *fmt_mode(mode_t mode)
'. Ввод представляет собой значение mode_t
, полученное из поля st_mode
структуры struct stat
; т.е. оно содержит как биты прав доступа, так и типа файла.
Вывод должен представлять строку в 10 символов, идентичную первому полю вывода 'ls -l
'. Другими словами, первый символ обозначает тип файла, а остальные девять – права доступа.
Когда установлены биты S_ISUID
и S_IXUSR
, используйте s
вместо x
; если установлен лишь бит I_ISUID
, используйте S
. То же относится к битам S_ISGID
и S_IXGRP
. Если установлены оба бита S_ISVTX
и S_IXOTH
, используйте t
; для одного S_ISVTX
используйте T
.
Для простоты можете использовать статический (static
) буфер, содержимое которого перезаписывается при каждом вызове процедуры.
2. Доработайте ch05-catdir.c
, чтобы она вызывала stat()
для каждого найденного имени файла. Затем выведите номер индекса, результат вызова fmt_mode()
, число ссылок и имя файла.
3. Доработайте ch05-catdir.c
так, что если файл является символической ссылкой, программа будет также выводить имя указываемого файла.
4. Добавьте такую опцию, что если имя файла является именем подкаталога, программа рекурсивно входит в него и отображает сведения о файлах (и каталогах) этого подкаталога. Необходим лишь один уровень рекурсии.
5. Если вы не работаете на системе GNU/Linux, запустите ch05-trymkdir
(см. раздел 5.2 «Создание и удаление каталогов») на своей системе и сравните результаты с приведенными нами.
6. Напишите программу mkdir
. Посмотрите свою локальную справочную страницу для mkdir(1) и реализуйте все ее опции.
7. В корневом каталоге, /
, как номер устройства, так и номер индекса для '.
' и '..
' совпадают. Используя эту информацию, напишите программу pwd
.
Вначале программа должна найти имя текущего каталога, прочитав содержимое родительского каталога. Затем она должна продолжить собирать сведения о иерархии файловой системы, пока не достигнет корневого каталога.
Отображение имени каталога в обратном порядке, от текущего каталога до корневого, легко. Как будет справляться ваша версия pwd
с выводом имени каталога правильным образом, от корневого каталога вниз?
8. Если вы написали pwd
, использовав рекурсию, напишите ее снова, использовав итерацию. Если вы использовали итерацию, напишите ее с использованием рекурсии. Что лучше? (Подсказка: рассмотрите очень глубоко вложенные деревья каталогов.)
9. Тщательно исследуйте функцию rpl_utime()
(см. раздел 5.5.3.1 «Подделка utime(file, NULL)
»). Какой ресурс не восстанавливается, если одна из проверок в середине if
не выполняется? (Спасибо Джеффу Колье (Geoff Collyer).)
10. (Трудное) Прочтите страницу справки для chmod(1). Напишите код для анализа аргумента символических опций, который позволяет добавлять, удалять и устанавливать права доступа на основе владельца, группы, других и «всех».
Когда вы решите, что это работает, напишите свою собственную версию chmod
, которая применяет назначенные права доступа к каждому файлу или каталогу, указанному в командной строке. Какую функцию вы использовали, chmod()
– или open()
и fchmod()
– и почему?
Глава 6
Общие библиотечные интерфейсы – часть 1
В главе 5 «Каталоги и служебные данные файлов» мы видели, что непосредственное чтение каталога возвращает имена файлов в том порядке, в каком они хранятся в каталоге. Мы также видели, что struct stat
содержит всю информацию о файле за исключением его имени. Однако, некоторые компоненты этой структуры не могут использоваться непосредственно; они являются просто числовыми значениями.
В данной главе представлена оставшаяся часть API, необходимая для полного использования значений компонентов struct stat
. Мы по порядку рассматриваем следующие темы: значения time_t
для представления времени и функций форматирования времени; функции сортировки и поиска (для сортировки имен файлов или других данных); типы uid_t
и gid_t
для представления пользователей, групп и функций, которые сопоставляют их с соответствующими именами пользователей и групп; и наконец, функцию для проверки того, что дескриптор файла представляет терминал.
Значения времени хранятся в типе, который известен как time_t
. Стандарт ISO С гарантирует, что это числовой тип, но во всем остальном никак не указывает, чем именно он является (целым или с плавающей точкой), как и не указывает степень точности хранящихся в нем значений.
На системах GNU/Linux и Unix значения time_t
представляют «секунды с начала Эпохи». Эпоха представляет собой начало записываемого времени, которое относится к полночи 1 января 1970 г. по UTC. На большинстве систем time_t
является long int
С. Для 32-разрядных систем это означает, что time_t
переполнится 19 января 2038 г. К тому времени, мы надеемся, тип time_t
будет переопределен как по меньшей мере 64-разрядное значение.
Для получения текущего времени, вычисления разницы между двумя значениями time_t
, преобразования значений time_t
в более удобное представление и форматирования обоих представлений в виде символьных строк существуют различные функции. Вдобавок, представление даты и времени можно преобразовать обратно в time_t
, доступна также ограниченная информация по часовым поясам.
Отдельный набор функций предоставляет доступ к текущему времени с разрешением, большим чем одна секунда. Функции работают с предоставлением двух различных значений, времени в виде секунд с начала Эпохи и числа микросекунд в текущей секунде. Эти функции описаны далее в разделе 14.3.1 «Время в микросекундах: gettimeofday()
».
time()
и difftime()
Системный вызов time()
получает текущие дату и время; difftime()
вычисляет разницу между двумя значениями time_t
:
#include
time_t time(time_t *t);
double difftime(time_t time1, time_t time0);
time()
возвращает текущее время. Если параметр t
не равен NULL
, переменная, на которую указывает t
, также заполняется значением текущего времени. Функция возвращает (time_t)(-1)
, если была ошибка, устанавливая errno
.
Хотя ISO С не указывает, чем является значение time_t
, POSIX определяет, что оно представляет время в секундах. Поэтому это предположение является обычным и переносимым. Например, чтобы посмотреть, что значение времени представляет отметку в прошлом шесть месяцев назад или позже, можно использовать код, подобный этому:
/* Для краткости проверка ошибок опущена */
time_t now, then, some_time;
time(&now); /* Получить текущее время */
then = now – (6L * 31 * 24 * 60 * 60); /* Примерно 6 месяцев назад */
/* ...установить какое-нибудь время, например, через stat()... */
if (some_time < then)
/* более 6 месяцев назад */
else
/* менее 6 месяцев назад */
Однако, поскольку переносимый код может потребоваться запустить на не-POSIX системах, существует функция difftime()
для вычисления разницы между двумя значениями времени. Тот же самый тест с использованием difftime()
можно было бы написать таким способом:
time_t now, some_value;
const double six_months = 6.0 * 31 * 24 * 60 * 60;
time(&now); /* Получить текущее время */
/* ...установить какое-нибудь время, например, через stat()... */
if (difftime(now, some_time) >= six_months)
/* более 6 месяцев назад */
else
/* менее 6 месяцев назад */
Возвращаемым типом difftime()
является double
, поскольку time_t
может также содержать доли секунд. На системах POSIX он всегда представляет целые секунды.
В обоих предыдущих примерах обратите внимание на использование типизированных констант, чтобы форсировать выполнение вычислений с нужным математическим типом: 6L
в первом случае для целых long
, 6.0 во втором случае для чисел с плавающей точкой
gmtime()
и localtime()
На практике форма представления даты и времени в виде «секунд с начала эпохи» не является очень удобной, кроме очень простых сравнений. Самостоятельное вычисление компонентов времени, таких, как месяц, день, год и т.д., подвержено ошибкам, поскольку необходимо принять во внимание местный часовой пояс (возможно, с учетом перехода на летнее время), правильно вычислить високосные годы и пр. К счастью, две стандартные процедуры делают за вас эту работу:
#include
struct tm *gmtime(const time_t *timep);
struct tm *localtime(const time_t *timep);
gmtime()
возвращает указатель на struct tm
, которая представляет время UTC. localtime()
возвращает указатель на struct tm
, представляющий местное время, т.е. в расчет берутся текущий часовой пояс и переход на летнее время. На самом деле это «время для настенных часов», дата и время, которые были бы отображены на настенных или ручных часах. (Как это работает, обсуждается далее в разделе 6.1.5 «Получение сведений о часовом поясе».)
Обе функции возвращают указатель на struct tm
, которая выглядит следующим образом:
struct tm {
int tm_sec; /* секунды */
int tm_min; /* минуты */
int tm_hour; /* часы */
int tm_mday; /* день месяца */
int tm_mon; /* месяц */
int tm_year; /* год */
int tm_wday; /* день недели */
int tm_yday; /* день в году */
int tm_isdst; /* летнее время */
};
struct tm
называют разложенным временем (broken-down time), поскольку значение time_t
«разложено» на свои составные части. Составные части, их диапазоны и значения показаны в табл. 6.1.
Таблица 6.1. Поля структуры tm
tm_sec | 0–60 | Секунда минуты. Секунда 60 предусматривает пропущенные (leap) секунды. (В C89 был диапазон 0–61.) |
tm_min | 0–59 | Минута часа. |
tm_hour | 0–23 | Час дня |
tm_mday | 1–31 | День месяца |
tm_mon | 0–11 | Месяц года |
tm_year | 0–N | Год, начиная с 1900 г. |
tm_wday | 0–6 | День недели, воскресенье = 0 |
tm_yday | 0–365 | День года, 1 января = 0. |
tm_isdst | <0, 0, >0 | Флаг летнего времени. |
Стандарт ISO С представляет большинство этих значений как «x
после y
». Например, tm_sec
является числом «секунд после минуты», tm_mon
«месяцев после января», tm_wday
«дней недели после воскресенья» и т.д. Это помогает понять, почему все значения начинаются с 0. (Единственным исключением, достаточно логичным, является tm_mday
, день месяца, имеющий диапазон 1–31.) Конечно, отсчет их с нуля также практичен; поскольку массивы С отсчитываются с нуля, использование этих значений в качестве индексов тривиально:
static const char *const days[] = { /* Массив имен дней */
"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday",
};
time_t now;
struct tm *curtime;
time(&now); /* Получить текущее время */
curtime = gmtime(&now); /* Разложить его */
printf("Day of the week: %sn", days[curtime->tm_wday]);
/* Проиндексировать и вывести */
Как gmtime()
, так и localtime()
возвращают указатель на struct tm
. Указатель указывает на static struct tm
, содержащуюся в каждой процедуре, и похоже, что эти структуры struct tm
переписываются каждый раз, когда вызываются процедуры. Поэтому хорошая мысль сделать копию возвращенной struct
. Возвращаясь к предыдущему примеру.
static const char *const days[] = { /* Как ранее */ };
time_t now;
struct tm curtime; /* Структура, а не указатель */
time(&now); /* Получить текущее время */
curtime = *gmtime(&now); /* Разложить его и скопировать данные */
printf("Day of the week: %sn", days[curtime.tm_wday]);
/* Проиндексировать и напечатать, использовать . , а не -> */
Поле tm_isdst
указывает, действует ли в настоящий момент летнее время (DST) Значение 0 означает, что DST не действует, положительное значение означает, что действует, а отрицательное значение – что информация о DST недоступна. (Стандарт С намеренно неконкретен, указывая лишь нулевое, положительное и отрицательное значения; это дает возможность большей свободы при реализации.)
Примеры в предыдущем разделе показали, как поля в struct tm
могли бы быть использованы в качестве индексов символьных строк для вывода информативных значений даты и времени. Хотя можно было бы написать собственный код, использующий такие массивы для форматирования даты и времени, стандартные процедуры облегчают работу
asctime()
и ctime()
Две первые стандартные процедуры, перечисленные ниже, выводят данные в фиксированном формате:
#include
char *asctime(const struct tm *tm);
char *ctime(const time_t *timep);
Как и в случае с gmtime()
и localtime()
, asctime()
и ctime()
возвращают указатели на статические буфера, которые могут быть перезаписаны после каждого вызова. Более того, эти две процедуры возвращают строки в одном и том же формате. Они отличаются лишь видом принимаемых аргументов, asctime()
и ctime()
должны использоваться тогда, когда все, что вам нужно, это простые сведения о дате и времени.
#include
#include
int main(void) {
time_t now;
time(&now);
printf("%s", ctime(& now));
}
После запуска эта программа выводит результат в виде: 'Thu May 22 15:44:21 2003
'. Завершающий символ конца строки включен в результат. Точнее, возвращаемое значение указывает на массив из 26 символов, как показано на рис. 6.1.
![](img_10.jpeg)
Рис. 6.1. Возвращаемая функциями ctime()
и asctime()
строка
Значительная часть старого кода Unix полагается на тот факт, что значения в возвращенной строке имеют фиксированную позицию. При использовании этих функций помните, что они включают завершающий символ конца строки. Поэтому наш небольшой пример программы использует для printf()
простую форматирующую строку "%s
", а не "%sn
", как можно было бы ожидать.
ctime()
устраняет необходимость шага вызова localtime()
; в сущности, это эквивалентно
time_t now;
char *curtime;
time(&now);
curtime = asctime(localtime(&now));
strftime()
Хотя часто достаточно использования asctime()
и ctime()
, у них есть также и ограничения:
• Формат вывода фиксирован. Нет способа изменить порядок элементов.
• В вывод не включаются сведения о часовом поясе.
• В выводе используются сокращенные названия месяца и дня.
• В выводе используются английские названия месяцев и дней.
По этим причинам C89 ввело стандартную библиотечную процедуру strftime()
:
#include
size_t strftime(char *s, size_t max, const char *format,
const struct tm *tm);
strftime()
сходна с sprintf()
. Ее аргументы следующие:
char *s
Буфер для форматированной строки.
size_t max
Размер буфера.
const char *format
Форматирующая строка.
const struct tm *tm
Указатель на struct tm
, представляющий разложенное время, которое надо отформатировать.
Форматирующая строка содержит символы букв, смешанные о описателями преобразования, указывающими, что должно быть помещено в строку, такими, как полное имя дня недели, час в соответствии с 24-часовым или 12-часовым циклом, наличие указателей am или p.m[64]64
a.m. – от ante meridiem (до полудня), p.m. – от post meridiem (пополудни), американская система обозначения 12-часового цикла времени суток. – Примеч. перев.
[Закрыть], и т.д. (Вскоре будут приведены примеры.)
Если всю строку можно отформатировать с использованием не более max
символов, возвращаемое значение представляет собой число символов, помещенных в s
, не включая завершающий нулевой байт. В противном случае, возвращаемое значение ноль. В последнем случае содержание s
«неопределенно». Следующий простой пример дает представление об использовании strftime()
:
#include
#include
int main(void) {
char buf[100];
time_t now;
struct tm *curtime;
time(&now);
curtime = localtime(&now);
(void)strftime(buf, sizeof buf,
"It is now %A, %B %d, %Y, %I:%M %p", curtime);
printf("%sn", buf);
exit(0);
}
После запуска эта программа выводит что-то типа:
It is now Thursday, May 22, 2003, 04:15 PM
В табл. 6.2 предоставлен полный список описателей преобразования, их возможные альтернативные представления и их значения. Вдобавок стандарт C99 добавил к списку дополнительные описатели; новые для C99 описатели помечены символом √.
Таблица 6.2. Описатели преобразования формата strftime()
%a | Локальное сокращенное название дня недели. | |
%A | Локальное полное название дня недели. | |
%b | Локальное сокращенное название месяца. | |
%B | Локальное полное название месяца. | |
%c, %Ec | Локальное «подходящее» представление даты и времени | |
%C, %EC | √ | Век (00–99) |
%d, %Od | День месяца (01–31) | |
%D | √ | То же, что %m/%d/%y |
%e, %Oe | √ | День месяца. Одна цифра дополняется пробелом (1–31). |
%F | √ | То же, что и %Y-%m-%d (формат даты ISO 8601) |
%g | √ | Две последние цифры, основанной на неделе года (00–99). |
%G | √ | Основанный на неделе год ISO 8601 |
%h | √ | То же, что и %b |
%H, %OH | Час в 24-часовом цикле (00–23) | |
%I, %OI | Час в 12-часовом цикле (01–12) | |
%j | День года (001–366) | |
%m, %Om | Месяц в виде числа (01–12). | |
%M, %OM | Минута в виде числа (00–59) | |
%n | √ | Символ конца строки ('n ') |
%p | Локальное обозначение a.m./p.m. | |
%r | √ | Локальное время в 12-часовом цикле |
%R | √ | Тоже, что и %H:%M |
%S, %OS | Секунда в виде числа (00–60) | |
%t | √ | Символ табуляции ('t ') |
%T | √ | То же, что и %H:%M:%S (формат ISO 8601). |
%u, %Ou | √ | Число дня недели ISO 8601, понедельник = 1 (1–7). |
%U, %OU | Номер недели, первое воскресенье является первым днем недели 1 (00–53) | |
%V, %OV | √ | Номер недели ISO 8601 (01–53) |
%w, %Ow | День недели как число, воскресенье = 0 (0–6). | |
%W, %OW | Номер недели, первый понедельник является первым днем недели 1 (00–53) | |
%x, %Ex | Локальное «подходящее» представление даты | |
%X, %EX | Локальное «подходящее» представление времени. | |
%y, %Ey, %Oy | Две последние цифры года (00–99) | |
%Y, %EY | Год как число. | |
%Z | Локальный часовой пояс или отсутствие символов, если сведения о часовом поясе недоступны | |
%% | Простой % |
Локаль (locale) является способом описания местной специфики, принимая во внимание такие вещи, как язык, кодировка символов и значения по умолчанию для форматирования дат, времени, денежных сумм и т.д. Мы будем иметь с ними дело в главе 13 «Интернационализация и локализация». Пока достаточно понять, что результаты strftime()
для одной и той же форматирующей строки могут быть разными в зависимости от настроек текущей локали.
Версии, начинающиеся с %E
и %O
, предназначены для «альтернативного представления». В некоторых локалях есть несколько способов представления одних и тех же сведений; эти описатели предоставляют доступ к дополнительным представлениям. Если определенная локаль не поддерживает альтернативного представления, strftime()
использует обычную версию.
Многие Unix-версии date
дают возможность предоставить в командной строке форматирующую строку, начинающуюся с символа '+
'. Затем date
форматирует текущие дату и время и выводит в соответствии с форматирующей строкой:
$ date + 'It is now %A, %B %d, %Y, %I:%M %p'
It is now Sunday, May 25, 2003, 06:44 PM
Большинство новых описателей C99 происходит от таких существующих реализаций date
Unix. Описатели %n
и %t
не являются в С абсолютно необходимыми, поскольку символы табуляции и конца строки могут быть помещены в строку непосредственно. Однако в контексте форматирующей строки date
для командной строки они имеют больше смысла. Поэтому они также включены в спецификацию strftime()
.
Стандарт ISO 8601 определяет (среди других вещей), как нумеруются недели в пределах года. В соответствии с этим стандартом недели отсчитываются с понедельника по воскресенье, а понедельник является днем недели 1, а не 0. Если неделя, в которой оказывается 1 января, содержит по крайней мере четыре дня нового года, она считается неделей 1. В противном случае, это последняя неделя предыдущего года с номером 52 или 53. Эти правила используются для вычислений описателей форматов %g
, %G
и %V
. (Хотя ограниченным американцам, таким, как автор, эти правила могут показаться странными, они обычно повсюду используются в Европе.)
Многие из описателей стандартов дают результаты, специфичные для текущей локали. Вдобавок некоторые указывают, что они выдают «подходящее» представление для локали (например, %x
). Стандарт C99 определяет значения для локали «С». Эти значения перечислены в табл. 6.3
Таблица 6.3. Значения локали «С» для определенных форматов strftime()
%а | Первые три символа %А . |
%А | Один из дней Sunday, Monday, …, Saturday |
%b | Первые три символа %В |
%В | Один из месяцев January, February, …, December |
%с | То же, что и %а %b %е %T %Y |
%p | AM или PM |
%r | То же, что и %I:%M:%S %p |
%x | То же, что и %m/%d/%y |
%X | То же, что и %T . |
%Z | Определяется реализацией |
Должно быть очевидно, что strftime()
предоставляет значительную гибкость и контроль над связанным с датой и временем выводом, во многом таким же образом, как printf()
и sprintf()
. Более того, strftime()
не может переполнить буфер, поскольку она проверяет входной параметр размера, что делает ее более безопасной процедурой, чем sprintf()
.
В качестве простого примера рассмотрим создание файлов журнала программы, когда каждый час создается новый файл. Имя файла должно включать дату и время создания:
/* Проверка ошибок для краткости опущена */
char fname[PATH_МАХ]; /* PATH_МАХ находится в
time_t now;
struct tm *tm;
int fd;
time(&now);
tm = localtime(&now);
strftime(fname, sizeof fname, "/var/log/myapp.%Y-%m-%d-%H:%M", tm);
fd = creat(name, 0600);
...
Формат год-месяц-день-час-минута вызывает сортировку файлов в том порядке, в каком они были созданы.
ЗАМЕЧАНИЕ. Некоторые форматы данных более полезны, чем другие. Например, 12-часовое время двусмысленно, также, как чисто числовые форматы дат. (Что означает '
9/11
'? Это зависит от того, где вы живете) Сходным образом, годы из двух цифр также являются плохой мыслью. Используйтеstrftime()
благоразумно