Задание. Разбор записки секретного агента
Создадим скрипт, который извлекает из текстового файла описание событий (в определённом формате), строит список этих событий и сообщает, сколько времени осталось до их наступления.
Постановка задачи
Имеется текстовый файл в кодировке UTF-8, содержащий некоторый произвольный текст. В этом тексте время от времени встречаются описания некоторых событий. Описание каждого события состоит из даты (и, возможно, времени) наступления этого события, а так же описания его. Доподлинно известно, что событие помещается в одну строку файла, то есть строки файла можно рассматривать как независимые (не может быть ситуации, что текст события начался на одной строке, а продолжился на другой). Текст события заканчивается на символ «.» (точка). Пример фрагмента файла (в этом тексте событием является выделенный текст):
Нужно запомнить следующее: 14.07.2020 в 14:00 объект вылетает из Сан-Франциско. Хорошо бы его перехватить.
В одной строке файла — не более одного события.
Таким образом, описание события начинается с даты события, за которой может следовать время события, а затем вплоть до символа «.» (точка) следует описание события. Именно эту часть строки надо извлечь, разделить на компоненты и сохранить в памяти для обработки.
После того, как все события получены из файла, нужно отсортировать их по дате и времени (от старых к новым), затем для каждого события выяснить, через сколько времени оно наступит (или сколько времени прошло с момента наступления, если событие из прошлого) и вывести на экран две таблицы: сначала грядущие события в порядке от старых к новым, затем прошедшие события от новых к старым (разный порядок сортировки). Перед каждой таблицей вывести заголовок (например, БУДУЩИЕ СОБЫТИЯ
и ПРОШЕДШИЕ СОБЫТИЯ
). Строка таблицы имеет вид:
75 days, 12:35:00 объект вылетает из Сан-Франциско.
То есть содержит информацию о промежутке времени до события (или после события) и описание события.
Представление данных
Дата
Серьёзная проблема: дата может быть задана разными способами. Дело в том, что люди часто пишут даты по-разному. Наш скрипт должен учитывать (как минимум) следующие представления даты:
ДД.ММ.ГГ
, то есть вроде «01.07.20» или «14.12.19». При этом будем считать, что имеются в виду годы 21 века, то есть 19 — это 2019, 65 — это 2065, и так далее. Просто приписываем перед годом «20».ДД.ММ.ГГГГ
, то есть вроде «05.09.2021» или «13.02.1987» — просто полный формат даты, принятый в России.Д месяц ГГГГ
, то есть вроде «12 апреля 1965» или «4 июля 2024». Здесь понадобится подключенная русскоязычная локаль.Д месяц
, то же самое, но без указания года, например, «17 мая» или «6 февраля». Рассматривать аналогично предыдущему, но в качестве года брать следующий год (в нашем случае 2020).
Поскольку при таком подходе искать в строке дату трудно, можно воспользоваться регулярными выражениями.
Время
Время у события может отсутствовать вовсе, в таком случае текст события следует сразу за датой через пробел. Если же время указано, время всегда записывается в формате в ЧЧ:ММ
, где ЧЧ
— часы в виде двух цифр (от 00 до 23), ММ
— минуты в виде двух цифр (от 00 до 59), в
— русская буква «в», предлог. То есть если после даты следует сочетание символов «в XX:XX
», значит, это время. В таком случае надо из строки достать время и использовать его. Описание события тогда следует через пробел после времени. Если же такого сочетания символов после даты не обнаружено, значит, время следует принять равным «00:00», а текст события брать через пробел от даты.
Описание (текст) события
Описание события — это любые символы, следующие после времени (или, если время не было указано, после даты), заканчивающиеся точкой. В самом тексте описания символ «.» (точка) никогда не встречается.
Данные для задачи
Пример файла с данными: https://anhel.in/files/secret_agent_data.txt. Скачайте его и попробуйте применить свой скрипт к этому файлу.
Советы по реализации
Идея примерно такая:
- Следует читать файл строка за строкой.
- В каждой прочитанной строке нужно проверить, содержится ли там событие.
- Если оно там есть, нужно извлечь из строки дату, время (если есть) и описание события и сохранить в список. Предлагаю сразу же преобразовывать текст даты и времени к объекту типа
datetime
и добавлять в список кортеж из двух значений: дата-время события и текст события. Кстати, если преобразование не удалось, следует продолжить поиск даты в строке. - Когда файл прочитан, следует закрыть его и разделить список событий на два списка: события, которые ещё не наступили (дата их находится в будущем по отношению к сегодняшней) и события, которые уже наступили (их дата находится в прошлом по отношению к сегодняшней).
- Каждый из этих списков следует отсортировать по дате, причём список с будущими событиями — по возрастанию, а список с прошлыми событиями — по убыванию.
- Наконец, нужно вывести эти списки (сначала список с будущими событиями, потом с прошедшими), не забыв снабдить их поясняющими заголовками.
Чтобы проверить наличие события в строке, следует попытаться найти в этой строке дату. Поскольку дата может быть в четырёх разных форматах, это надо делать четырежды. Проще всего воспользоваться регулярными выражениями в количестве четырёх штук, по одному на каждый формат.
Если в строке обнаружена дата в одном из форматов, надо достать дату из строки (через группы захвата регулярных выражений или с помощью срезки) в отдельную строковую переменную. Затем следует превратить дату в объект типа date
, воспользовавшись методом datetime.strptime
с соответствующим описанием формата.
После этого надо проверить, не стоит ли время сразу после даты (через пробел). Для этого нужно проверить оставшуюся часть строки на совпадение с регулярным выражением, соответствующим строке «в ХХ:ХХ», где ХХ — цифры. Если совпадение успешно, следует извлечь время, превратить его в объект типа time
(например, с помощью функции datetime.strptime
). Если совпадение не удалось, нужно создать объект time
с временем 00:00:00
. Затем следует собрать ранее полученную дату и это время в объект datetime
, используя функцию datetime.combine
.
Наконец, нужно найти в остатке строки символ точки и вырезать описание события (от времени до точки или от даты до точки).
Для сортировки списков по дате следует воспользоваться методом списка sort
с аргументом key=
. Поскольку метод sort
всегда сортирует список по возрастанию, для сортировки по убыванию после использования метода sort
следует использовать метод reverse
, чтобы перевернуть отсортированный список в обратном порядке.
Напомню, что при выводе нужно выводить не дату-время события, а информацию о том, через сколько дней, часов и минут событие наступит, или же сколько времени прошло с его наступления.