Текст книги "Основы программирования в Linux"
Автор книги: Нейл Мэтью
Соавторы: Ричард Стоунс
Жанры:
Программирование
,сообщить о нарушении
Текущая страница: 15 (всего у книги 67 страниц)
Для того чтобы вам легче было следовать правилам, приведенным в этих руководствах, ОС Linux предлагает очень простое в применении средство getopt
, поддерживающее использование опций со значениями и без них.
#include
int getopt(int argc, char *const argv[], const char *optstring);
extern char *optarg;
extern int optind, opterr, optopt;
Функция getopt
принимает параметры argc
и argv
в том виде, в каком они передаются функции main
в программе, и строку спецификатора опций, которая сообщает getopt
, какие опции определены для программы и есть ли у них связанные с ними значения. optstring
– это просто список символов, каждый из которых представляет односимвольную опцию. Если за символом следует двоеточие, это означает, что у опции есть ассоциированное значение, которое будет принято как следующий аргумент. Команда getopt
оболочки bash выполняет аналогичную функцию.
Например, для обработки предыдущего примера можно было бы применить следующий вызов:
getopt(argc, argv, «if:lr»);
В нем учтены простые опции -i
, -l
, -r
и -f
, за которыми последует аргумент с именем файла. Вызов команды с теми же параметрами, но указанными в другом порядке, изменит поведение. Вы сможете попробовать сделать это, когда получите пример кода из упражнения 4.2.
Результат, возвращаемый функцией getopt
, – символ следующей опции, хранящийся в массиве argv
(если он есть). Вызывайте getopt
повторно для поочередного получения каждой опции. Функция ведет себя следующим образом.
□ Если опция принимает значение, на него указывает внешняя переменная optarg
.
□ Функция getopt
вернет -1, когда не останется опций для обработки. Специальный аргумент –
заставит getopt
прекратить перебор опций.
□ Функция getopt
вернет ?
, если есть нераспознанная опция, которую она сохранит во внешней переменной optopt
.
□ Если опции требуется значение (например, в нашем примере опции -f
) и не задана никакая величина, getopt
обычно возвращает ?
. Если поместить двоеточие как первый символ в строке опций, при отсутствии заданной величины функция getopt
вернет :
вместо ?
.
Во внешней переменной optind
хранится номер следующего обрабатываемого аргумента. Функция getopt
использует ее, чтобы знать, как далеко она продвинулась. Программы редко нуждаются в установке этой переменной. Когда все аргументы с опциями обработаны, переменная optind
указывает, где в конце массива argv можно найти оставшиеся аргументы.
Некоторые версии функции getopt
прекратят выполнение при обнаружении первого аргумента не опции, вернув значение -1 и установив переменную optind
. Другие, например предлагаемые в ОС Linux, могут обрабатывать опции, где бы они ни встретились в аргументах программы. Учтите, что в данном случае getopt
фактически перепишет массив argv
так, что все аргументы не опции будут собраны вместе, начиная с элемента массива argv[optind]
. В случае версии GNU функции getopt
ее поведение определяется переменной окружения POSIXLY_CORRECT
. Если переменная установлена, getopt
остановится на первом аргументе не опции. Кроме того, некоторые реализации getopt
выводят сообщения об ошибке для незнакомых опций. Имейте в виду, что в стандарте POSIX написано о том, что если переменная opterr
не равна нулю, функция getopt
выведет сообщение об ошибке в stderr
.
Итак, выполните упражнение 4.2.
Упражнение 4.2. Функция getopt
В этом упражнении вы используете функцию getopt; назовите новую программу argopt.c.
#include
#include
#include
int main(int argc, char *argv[]) {
int opt;
while ((opt = getopt(argc, argv, «:if:lr»)) != -1) {
switch(opt) {
case 'i':
case 'l':
case 'r':
printf(«option: %cn», opt);
break;
case 'f':
printf(«filename: %sn», optarg);
break;
case ':':
printf(«option needs a valuen»);
break;
case '?':
printf(«unknown option: %cn», optopt);
break;
}
}
for (; optind < argc; optind++)
printf(«argument: %sn», argv[optind]);
exit(0);
}
Теперь, когда вы выполните программу, то увидите, что все аргументы командной строки обрабатываются автоматически:
$ ./argopt -i -lr 'hi there' -f fred.с -q
option: i
option: l
option: r
filename: fred.c
unknown option: q
argument: hi there
Как это работает
Программа многократно вызывает функцию getopt
для обработки аргументов-опций до тех пор, пока не останется ни одного, в этот момент getopt
вернет -1. Для каждой опции выбирается подходящее действие, включая обработку неизвестных опций и пропущенных значений. Если у вас другая версия getopt
, то вы получите вывод, слегка отличающийся от показанного, – особенно сообщения об ошибках – но смысл будет понятен.
Когда все опции обработаны, программа просто выводит оставшиеся аргументы, как и раньше, но начиная с номера, хранящегося в переменной optind
.
Многие приложения Linux принимают более информативные аргументы, чем использованные в предыдущем примере односимвольные опции. Библиотека С проекта GNU содержит версию функции getopt
, названную getopt_long
, которая принимает так называемые длинные аргументы, которые вводятся с помощью двойного дефиса.
Рассмотрим упражнение 4.3.
Упражнение 4.3. Функция getopt_long
Примените функцию getopt_long
для создания новой версии примера программы, которая может вызываться с использованием длинных эквивалентов опций, например, следующих:
$ ./longopt –initialize –list 'hi there' –file fred.c -q
option: i
option: l
filename: fred.c
./longopt: invalid option –q
unknown option: q
argument: hi there
На самом деле и новые длинные опции, и исходные односимвольные можно смешивать. Длинным опциям также можно давать сокращенные названия, но они
должны отличаться от односимвольных опций. Длинные опции с аргументом можно задавать как единый аргумент в виде –опция= значение, как показано далее:
$ ./longopt –init -l –file=fred.с 'hi there'
option: i
option: l
filename: fred.с
argument: hi there
Далее приведена новая программа longopt.c, полученная из программы argopt.c с изменениями, обеспечивающими поддержку длинных опций, которые в тексте программы выделены цветом.
#include
#include
#include
#define _GNU_SOURCE
#include
int main(int argc, char *argv[]) {
int opt;
struct option_longopts[] = {
{"initialize", 0. NULL, 'i'},
{"file" 1, NULL, 'f'},
{"list", 0, NULL, 'l'},
{0, 0, 0, 0}};
while ((opt = getopt_long(argc, argv, ":if:lr, longopts, NULL)) != -1) {
switch(opt) {
case 'i':
case 'l':
case 'r':
printf(«option: %cn», opt);
break;
case 'f':
printf(«filename: %sn», optarg);
break;
case ':':
printf(«option needs a valuen»);
break;
case '?':
printf(«unknown option: %cn», optopt);
break;
}
}
for (; optind < argc; optind++)
printf(«argument: %sn», argv[optind]);
exit(0);
}
Как это работает
Функция getopt_long
принимает два дополнительных параметра по сравнению с функцией getopt
. Первый из них – массив структур, описывающий длинные опции и сообщающий функции getopt_long
способ их обработки. Второй дополнительный параметр – адрес переменной, которая может использоваться как вариант optind
, предназначенный для длинных опций; для каждой распознанной длинной опции ее номер в массиве длинных опций может быть записан в эту переменную. В данном примере вам не нужна эта информация, поэтому вы используете NULL
в качестве значения второго дополнительного параметра.
Массив длинных опций состоит из ряда структур типа struct option
, в каждой из которых описано требуемое поведение длинной опции. Массив должен заканчиваться структурой, содержащей все нули.
Структура длинной опции определена в заголовочном файле getopt.h и должна подключаться с помощью константы _GNU_SOURCE
, определенной для того, чтобы разрешить использование функции getopt_long
.
struct option {
const char *name;
int has_arg;
int *flag;
int val;
};
Элементы структуры описаны в табл. 4.1.
Таблица 4.1.
name | Название длинной опции. Сокращения будут приниматься до тех пор, пока они не создадут путаницы при определении названий других опций |
has_arg | Принимает ли эта опция аргумент. Задайте 0 для опций без аргументов, 1 для опций, у которых должно быть значение, и 2 для опций с необязательным аргументом |
flag | Задайте NULL , чтобы getopt_long вернула при обнаружении данной опции значение, заданное в val . В противном случае getopt_long возвращает 0 и записывает значение val в переменную, на которую указывает flag |
val | Значение getopt_long для данной опции, предназначенное для возврата |
Для получения сведений о других опциях, связанных с расширениями функции getopt
в проекте GNU и родственных функциях, см. страницы интерактивного справочного руководства к функции getopt
.
Переменные окружения
Мы обсуждали переменные окружения в главе 2. Это переменные, которые могут использоваться для управления поведением сценариев командной оболочки и других программ. Вы также можете применять их для настройки пользовательской среды. Например, у каждого пользователя есть переменная окружения HOME
, определяющая его исходный каталог, стандартное место старта его или ее сеанса. Как вы видели, просмотреть переменные окружения можно из строки приглашения командной оболочки:
$ echo $НOМЕ
/home/neil
Вы также можете воспользоваться командой оболочки set
для получения списка всех переменных окружения.
В спецификации UNIX определено множество стандартных переменных окружения, применяемых для самых разных целей, включая тип терминала, имена редакторов, установленных по умолчанию, названия часовых поясов и т.д. Программа на языке С может получить доступ к переменным окружения с помощью функций putenv
и getenv
.
#include
char *getenv(const char *name);
int putenv(const char *string);
Окружение состоит из строк видаимя=значение
. Функция getenv
ищет в окружении строку с заданным именем и возвращает значение, ассоциированное с этим именем. Она вернет NULL
, если требуемая переменная не существует. Если переменная есть, но ее значение не задано, функция getenv
завершится успешно и вернет пустую строку, в которой первый байт равен NULL
. Строка, возвращаемая getenv
, хранится в статической памяти, принадлежащей функции, поэтому для ее дальнейшего использования вы должны скопировать эту строку в другую, поскольку она может быть перезаписана при последующих вызовах функции getenv
.
Функция putenv
принимает строку вида имя=значение
и добавляет ее в текущее окружение. Она даст сбой и вернет -1, если не сможет расширить окружение из-за нехватки свободной памяти. Когда это произойдет, переменной errno
будет присвоено значение ENOMEM
.
В упражнении 4.4 вы напишeте программу для вывода значения любой выбранной вами переменной окружения. У вас также будет возможность задать значение, если вы укажете второй аргумент программы.
Упражнение 4.4. Функции getenv
и putenv
1. Первые несколько строк после объявления функции main
гарантируют корректный вызов программы environ.c с только одним или двумя аргументами:
#include
#include
#include
int main(int argc, char *argv[]) {
char *var, *value;
if (argc == 1 || argc > 3) {
fprintf(stderr, «usage: environ var [value]n»);
exit(1);
}
2. Сделав это, вы извлекаете значение переменной из окружения с помощью функции getenv
:
var = argv[1];
value = getenv(var);
if (value)
printf(«Variable %s has value %sn», var, value);
else
printf(«Variable %s has no valuen», var);
3. Далее проверьте, был ли при вызове программы указан второй параметр. Если был, вы задаете значение этого аргумента, конструируя строку вида имя=значение
и затем вызывая функцию putenv
:
if (argc == 3) {
char *string;
value = argv[2];
string = malloc(strlen(var)+strlen(value)+2);
if (!string} {
fprintf(stderr, «out of memoryn»);
exit(1);
}
strcpy(string, var);
strcat(string, "=");
strcat(string, value);
printf(«Calling putenv with: %sn», string);
if (putenv(string) != 0) {
fprintf(stderr, «putenv failedn»);
free(string);
exit(1);
}
4. В заключение вы узнаете новое значение переменной, вызвав функцию getenv еще раз:
value = getenv(var);
if (value)
printf(«New value of %s is %sn», var, value);
else
printf(«New value of %s is null??n», var);
}
exit(0);
}
Когда вы выполните эту программу, то сможете увидеть и задать переменные окружения:
$ ./environ НОМЕ
Variable HOME has value /home/neil
$ ./environ FRED
Variable FRED has no value
$ ./environ FRED hello
Variable FRED has no value
Calling putenv with: FRED=hello
New value of FRED is hello
$ ./environ FRED
Variable FRED has no value
Обратите внимание на то, что окружение локально по отношению к программе. Изменения, которые вы делаете в программе, не отражаются вне ее, поскольку значения переменных не передаются из дочернего процесса (вашей программы) в родительский (командную оболочку).
Применение переменных окруженияПрограммы часто применяют переменные окружения для изменения способа своей работы. Пользователи могут задать значения этих переменных окружения либо в их стандартном окружении с помощью файла .profile, читаемого их регистрационной командной оболочкой, использующей специальный файл запуска (rc) оболочки, либо заданием переменных в командной строке командной оболочки. Например,
$ ./environ FRED
Variable FRED has no value
$ FRED=hello ./environ FRED
Variable FRED has value hello
Командная оболочка принимает начальные присвоения значений переменным как временные изменения переменных окружения. Во второй части предыдущего примера программа environ
выполняется в окружении, где у переменной FRED
есть значение.
Например, в будущей версии приложения, управляющего базой данных компакт-дисков, вы сможете изменить переменную окружения, скажем CDDB
, обозначающую базу данных, которую нужно использовать. Каждый пользователь затем сможет задать собственное значение по умолчанию или применить команду оболочки для задания значения при очередном выполнении приложения:
$ CDDB=mycds; export CDDB
$ cdapp
или
$ CDDB=mycds cdapp
Переменная environПримечание
Переменные окружения – противоречивое благо, и их следует применять с осторожностью. Эти переменные более «закрыты» от пользователя, чем опции командной строки, и это может затруднить отладку. По смыслу переменные окружения подобны глобальным переменным, поэтому они могут изменять поведение программы, что порой приводит к неожиданным результатам.
Как вы уже знаете, окружение программы формируется из строк вида имя=значение. Этот массив строк становится доступен программе непосредственно из переменной environ, которая объявляется, как
#include
extern char **environ;
Выполните упражнение 4.5.
Упражнение 4.5. Переменная environ
Далее приведена программа showenv.c, использующая переменную environ для вывода переменных окружения.
#include
#include
extern char **environ;
int main() {
char **env = environ;
while (*env) {
printf(«%sn», *env);
env++;
}
exit(0);
}
Когда вы выполните программу в системе Linux, то получите нечто, похожее на следующий вывод, который немного сокращен. Количество, порядок отображения и значения этих переменных зависят от версии операционной системы, применяемой командной оболочки и настроек пользователя в момент выполнения программы.
$ ./showenv
HOSTNAME=tilde.provider.com
LOGNAME=neil
MAIL=/var/spool/mail/neil
TERM=xterm
HOSTTYPE=i386
PATH=/usr/local/bin:/bin:/usr/bin:
HOME=/usr/neil
LS_OPTIONS=-N –color=tty -T 0
SHELL=/bin/bash
OSTYPE=Linux
...
Как это работает
Для вывода всего окружения программа в цикле обращается к переменной environ
– массиву нуль-терминированных строк.
Время и дата
Программе часто полезно иметь возможность определить время и дату. Возможно, она хочет зарегистрировать длительность собственного выполнения или ей нужно изменять свое поведение в определенные моменты времени. Например, игра может отказываться запускаться в рабочие часы или программа резервного копирования по расписанию хочет дождаться ранних часов, прежде чем начать резервное копирование в автоматическом режиме.
Примечание
Во всех системах UNIX применяется одна и та же точка отсчета времени и дат: полночь по Гринвичу (GMT) на 1 января 1970 г. Это «начало эпохи UNIX», и ОС Linux – не исключение. Время в системе Linux измеряется в секундах, начиная с этого момента времени. Такой способ обработки аналогичен принятому в системе MS-DOS за исключением того, что эпоха MS-DOS началась в 1980 г. В других системах применяют точки отсчета иных эпох.
Время задается с помощью типа time_t
. Это целочисленный тип, достаточный для хранения дат и времени в секундах. В Linux-подобных системах это тип long integer
(длинное целое), определенный вместе с функциями, предназначенными для обработки значений времени, в заголовочном файле time.h.
Примечание
Не думайте, что для хранения времени достаточно 32 битов. В системах UNIX и Linux, использующих 32-разрядный тип
time_t
, временное значение «будет превышено» в 2038 г. Мы надеемся, что к тому времени системы перейдут на типtime_t
, содержащий более 32 битов. Недавнее широкое внедрение 64-разрядных процессоров превращает это практически в неизбежность.
#include
time_t time(time_t *tloc);
Вы можете найти низкоуровневое значение времени, вызвав функцию time
, которая вернет количество секунд с начала эпохи (упражнение 4.6). Она также запишет возвращаемое значение по адресу памяти, на который указывает параметр tloc
, если он – непустой указатель.
Упражнение 4. Функция time
Далее для демонстрации функции time приведена простая программа envtime.c.
#include
#include
#include
#include
int main() {
int i;
time_t the_time;
for (i = 1; i <= 10; i++) {
the_time = time((time_t *)0);
printf(«The time is %ldn», the_time);
sleep(2);
}
exit(0);
}
Когда вы запустите программу, она будет выводить низкоуровневое значение времени каждые 2 секунды в течение 20 секунд.
$ ./anytime
The time is 1179643852
The time is 1179643854
The time is 1179643856
The time is 1179643858
The time is 1179643860
The time is 1179643862
The time is 1179643864
The time is 1179643866
The time is 1179643868
The time is 1179643870
Как это работает
Программа вызывает функцию time
с пустым указателем в качестве аргумента, которая возвращает время и дату как количество секунд. Программа засыпает на две секунды и повторяет вызов time в целом 10 раз.
Использование времени и даты в виде количества секунд, прошедших с начала 1970 г., может быть полезно для измерения длительности чего-либо. Вы сможете сосчитать простую разность значений, полученных из двух вызовов функции time
. Однако комитет, разрабатывавший стандарт языка ISO/ANSI С, в своих решениях не указал, что тип time_t
будет применяться для определения произвольных интервалов времени в секундах, поэтому была придумана функция difftime
, которая вычисляет разность в секундах между двумя значениями типа time_t
и возвращает ее как величину типа double
:
#include
double difftime(time_t time1, time_t time2);
Функция difftime
вычисляет разницу между двумя временными значениями и возвращает величину, эквивалентную выражениювремя1–время2
, как число с плавающей точкой. В ОС Linux значение, возвращаемое функцией time
, – это количество секунд, которое может обрабатываться, но для максимальной переносимости следует применять функцию difftime
.
Для представления времени и даты в более осмысленном (с человеческой точки зрения) виде мы должны преобразовать значение времени в понятные время и дату. Для этого существуют стандартные функции.
Функция gmtime
подразделяет низкоуровневое значение времени на структуру, содержащую более привычные поля:
#include
struct tm *gmtime(const time_t timeval)
В структуре tm
, как минимум, определены элементы, перечисленные в табл. 4.2.
Таблица 4.2
tm | |
---|---|
int tm_sec | Секунды, 0–61 |
int tm_min | Минуты, 0–59 |
int tm_hour | Часы, 0–23 |
int tm_mday | День в месяце, 1–31 |
int tm_mon | Месяц в году, 0–11 (January (январь) соответствует 0) |
int tm_year | Годы, начиная с 1900 г. |
int tm_wday | День недели, 0–6 (Sunday (воскресенье) соответствует 0) |
int tm_yday | День в году, 0–365 |
int tm_isdst | Действующее летнее время |
Диапазон элемента tm_sec
допускает появление время от времени корректировочной секунды или удвоенной корректировочной секунды.
Выполните упражнение 4.7.
Упражнение 4.7. Функция gmtime
Далее приведена программа gmtime.с, выводящая текущие время и дату с помощью структуры tm
и функции gmtime
.
#include
#include
#include
int main() {
struct tm *tm_ptr;
time_t the_time;
(void)time(&the_time);
tm_ptr = gmtime(&the_time);
printf(«Raw time is %ldn», the_time);
printf(«gmtime gives:n»);
printf(«date: %02d/%02d/%02dn»,
tm_ptr->tm_year, tm_ptr->tm_mon+1, tm_ptr->tm_mday);
printf(«time: %02d:%02d:%02dn»,
tm_ptr->tm_hour, tm_ptr->tm_min, tm_ptr->tm_sec);
exit(0);
}
Выполнив эту программу, вы получите хорошее соответствие текущим времени и дате:
$ ./gmtime; date
Raw time is 1179644196
gmtime gives:
date: 107/05/20
time: 06:56:36
Sun May 20 07:56:37 BST 2007
Как это работает
Программа вызывает функцию time
для получения машинного представления значения времени и затем вызывает функцию gmtime для преобразования его в структуру с удобными для восприятия значениями времени и даты. Она выводит на экран полученные значения с помощью функции printf
. Строго говоря, выводить необработанное значение времени таким способом не следует, потому что наличие типа длинного целого не гарантировано во всех системах. Если сразу же после вызова функции gmtime выполнить команду date, можно сравнить оба вывода.
Но здесь у вас возникнет небольшая проблема. Если вы запустите эту программу в часовом поясе, отличном от Greenwich Mean Time (время по Гринвичу) или у вас действует летнее время, как у нас, вы заметите, что время (и, возможно, дата) неправильное. Все дело в том, что функция gmtime
возвращает время по Гринвичу (теперь называемое Universal Coordinated Time (всеобщее скоординированное время) или UTC). Системы Linux и UNIX поступают так для синхронизации всех программ и систем в мире. Файлы, созданные в один и тот же момент в разных часовых поясах, будут отображаться с одинаковым временем создания. Для того чтобы посмотреть местное время, следует применять функцию localtime
.
#include
struct tm *localtime(const time_t *timeval);
Функция localtime
идентична функции gmtime
за исключением того, что она возвращает структуру, содержащую значения с поправками на местный часовой пояс и действующее летнее время. Если вы выполните программу gmtime
, но замените все вызовы функции gmtime
на вызовы localtime
, в отчете программы вы увидите правильные время и дату.
Для преобразования разделенной на элементы структуры tm в общее внутреннее значение времени можно применить функцию mktime
:
#include
time_t mktime(struct tm *timeptr);
Функция mktime
вернет -1, если структура не может быть представлена как значение типа time_t
.
Для вывода программой date
«дружественных» (в противоположность машинному) времени и даты можно воспользоваться функциями asctime
и ctime
:
#include
char *asctime(const struct tm *timeptr);
char *ctime(const time_t *timeval);
Функция asctime
возвращает строку, представляющую время и дату, заданные tm
-структурой timeptr
. У возвращаемой строки формат, подобный приведенному далее:
Sun Jun 9 12:34:56 2007n
У нее всегда фиксированный формат длиной 26 символов. Функция ctime
эквивалентна следующему вызову:
asctime(localtime(timeval))
Она принимает необработанное машинное значение времени и преобразует его в местное время.
А теперь выполните упражнение 4.8.
Упражнение 4.8. Функция ctime
В этом примере благодаря приведенному далее программному коду вы увидите функцию ctime
в действии.
#include
#include
#include
int main() {
time_t timeval;
(void)time(&timeval);
printf («The date is: %s», ctime(&timeval));
exit(0);
}
Откомпилируйте и затем запустите на выполнение ctime.c, и вы увидите нечто похожее на приведенные далее строки:
$ ./ctime
The date is: Sat Jun 9 08:02:08 2007.
Как это работает
Программа ctime.c вызывает функцию time
для получения машинного значения времени и дает возможность функции ctime
выполнить всю тяжелую работу по преобразованию этого значения в удобочитаемую строку, которую потом и выводит на экран.
Для лучшего управления точным форматированием времени и даты ОС Linux и современные UNIX-подобные системы предоставляют функцию strftime
. Она довольно похожа на функцию sprintf
для дат и времени и действует аналогичным образом:
#include
size_t strftime(char *s, size_t maxsize, const char *format, struct tm *timeptr);
Функция strftime
форматирует время и дату, представленные в структуре tm
, на которую указывает параметр, timeptr
, и помещает результат в строку s
. Эта строка задается длиной maxsize
(как минимум) символов. Строка format
применяется для управления символами, записываемыми в строку. Как и в функции printf
, она содержит обычные символы, которые будут переданы в строку, и спецификаторы преобразований для форматирования элементов времени и даты. В табл. 4.3 перечислены используемые спецификаторы преобразований.
Таблица 4.3
%a | Сокращенное название дня недели |
%А | Полное название дня недели |
%b | Сокращенное название месяца |
%B | Полное название месяца |
%c | Дата и время |
%d | День месяца, 01–31 |
%H | Час, 00–23 |
%I | Час по 12-часовой шкале, 01–12 |
%j | День в году, 001–366 |
%m | Номер месяца в году, 01–12 |
%M | Минуты, 00–59 |
%p | a.m. (до полудня) или p.m. (после полудня) |
%S | Секунды, 00–59 |
%u | Номер дня недели, 1–7 (1 соответствует понедельнику) |
%U | Номер недели в году, 01–53 (воскресенье – первый день недели) |
%V | Номер недели в году, 01–53 (понедельник – первый день недели) |
%w | Номер дня недели, 0–6 (0 соответствует воскресенью) |
%x | Дата в региональном формате |
%X | Время в региональном формате |
%y | Номер года, меньший 1900 |
%Y | Год |
%Z | Название часового пояса |
%% | Символ % |
Таким образом, обычная дата, такая же, как полученная из программы date, соответствует следующей строке формата функции strftime
:
«%a %b %d %Н: %М: %S %Y»
Для облегчения чтения дат можно использовать функцию strptime
, принимающую строку с датой и временем и формирующую структуру tm
с теми же датой и временем:
#include
char *strptime(const char *buf, const char *format, struct tm *timeptr);
Строка format
конструируется точно так же, как одноименная строка функции strftime
. Функций strptime
действует аналогично функции sscanf
: она сканирует строку в поиске опознаваемых полей и записывает их в переменные. В данном случае это элементы структуры tm
, которая заполняется в соответствии со строкой format
. Однако спецификаторы преобразований для strptime
немного мягче спецификаторов функции strftime
. Так, в функции strptime
разрешены как сокращенные, так и полные названия дней и месяцев. Любое из этих представлений будет соответствовать спецификатору %a функции strptime
. Кроме того, в то время как функция strftime
для представления чисел, меньших 10, всегда применяет ведущие нули, strptime
считает их необязательными.
Функция strptime
возвращает указатель на символ, следующий за последним, обработанным в процессе преобразования. Если она встречает символы, которые не могут быть преобразованы, в этой точке преобразование просто прекращается. Для того чтобы убедиться в том, что в структуру tm
записаны значимые данные, вызывающей программе следует проверять, достаточно ли символов строки принято и обработано.
Рассмотрим работу функций на примере (упражнение 4.9).
Упражнение 4.9. Функции strftime
и strptime
Обратите внимание на выбор спецификаторов преобразований, использованных в следующей программе:
#include
#include
#include
int main() {
struct tm *tm_ptr, timestruct;
time_t the_time;
char buf[256];
char *result;
(void)time(&the_time);
tm_ptr = localtime(&the_time);
strftime(buf, 256, «%A %d %B, %I:%S %p», tm_ptr);
printf(«strftime gives: %sn», buf);
strcpy(buf, «Thu 26 July 2007, 17:53 will do fine»);
printf(«calling strptime with: %sn», buf);
tm_ptr = ×truct;
result = strptime(buf, «%a %d %b %Y, %R», tm_ptr);
printf(«strptime consumed up to: %sn», result);
printf(«strptime gives:n»);
printf («date: %02d/%02d/%02dn»,
tm_ptr->tm_year % 100, tm_ptr->tm_mon+1, tm_ptr->tm_mday);
printf(«time: %02d:%02dn»,
tm_ptr->tm_hour, tm->ptr->tm_min);
exit(0);
}
Когда вы откомпилируете и выполните программу strftime.c, то получите следующий результат:
$ ./strftime
strftime gives: Saturday 09 June, 08:16 AM
calling strptime with: Thu 26 July 2007, 17:53 will do fine
strptime concurred up to: will do fine
strptime gives:
date: 07/07/26
time: 17:53
Как это работает
Программа strftime получает текущее местное время с помощью вызовов функций time
и localtime
. Затем она преобразует его в удобочитаемую форму с помощью функции strftime
с подходящим аргументом форматирования. Для демонстрации применения функции strptime
программа задает строку, содержащую дату и время, затем вызывает strptime для извлечения необработанных значений времени и даты и выводит их на экран. Спецификатор преобразования %R
функции strptime
– это сокращенное обозначение комбинации %Н:%M
.
Важно отметить, что для успешного просмотра даты функции strptime необходима точная строка формата. Обычно она не может точно обработать даты, считываемые из строк, введенных пользователями, до тех пор, пока не будет строго выверен формат.
Возможно, при компиляции программы strftime.c вы получите предупреждение компилятора. Причина в том, что по умолчанию в библиотеке GNU не объявлена функция strptime
. Для устранения проблемы следует явно запросить средства стандарта X/Open, добавив следующую строку перед заголовочным файлом time.h:
#define _XOPEN_SOURCE