Язык программирования Python

Курс IT-Академии Сухорукова

Декабрь 2019 - январь 2020

Этот курс закончился

Задание. Разбор записки секретного агента

Создадим скрипт, который извлекает из текстового файла описание событий (в определённом формате), строит список этих событий и сообщает, сколько времени осталось до их наступления.

Постановка задачи

Имеется текстовый файл в кодировке 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. Скачайте его и попробуйте применить свой скрипт к этому файлу.

Советы по реализации

Идея примерно такая:

  1. Следует читать файл строка за строкой.
  2. В каждой прочитанной строке нужно проверить, содержится ли там событие.
  3. Если оно там есть, нужно извлечь из строки дату, время (если есть) и описание события и сохранить в список. Предлагаю сразу же преобразовывать текст даты и времени к объекту типа datetime и добавлять в список кортеж из двух значений: дата-время события и текст события. Кстати, если преобразование не удалось, следует продолжить поиск даты в строке.
  4. Когда файл прочитан, следует закрыть его и разделить список событий на два списка: события, которые ещё не наступили (дата их находится в будущем по отношению к сегодняшней) и события, которые уже наступили (их дата находится в прошлом по отношению к сегодняшней).
  5. Каждый из этих списков следует отсортировать по дате, причём список с будущими событиями — по возрастанию, а список с прошлыми событиями — по убыванию.
  6. Наконец, нужно вывести эти списки (сначала список с будущими событиями, потом с прошедшими), не забыв снабдить их поясняющими заголовками.

Чтобы проверить наличие события в строке, следует попытаться найти в этой строке дату. Поскольку дата может быть в четырёх разных форматах, это надо делать четырежды. Проще всего воспользоваться регулярными выражениями в количестве четырёх штук, по одному на каждый формат.

Если в строке обнаружена дата в одном из форматов, надо достать дату из строки (через группы захвата регулярных выражений или с помощью срезки) в отдельную строковую переменную. Затем следует превратить дату в объект типа date, воспользовавшись методом datetime.strptime с соответствующим описанием формата.

После этого надо проверить, не стоит ли время сразу после даты (через пробел). Для этого нужно проверить оставшуюся часть строки на совпадение с регулярным выражением, соответствующим строке «в ХХ:ХХ», где ХХ — цифры. Если совпадение успешно, следует извлечь время, превратить его в объект типа time (например, с помощью функции datetime.strptime). Если совпадение не удалось, нужно создать объект time с временем 00:00:00. Затем следует собрать ранее полученную дату и это время в объект datetime, используя функцию datetime.combine.

Наконец, нужно найти в остатке строки символ точки и вырезать описание события (от времени до точки или от даты до точки).

Для сортировки списков по дате следует воспользоваться методом списка sort с аргументом key=. Поскольку метод sort всегда сортирует список по возрастанию, для сортировки по убыванию после использования метода sort следует использовать метод reverse, чтобы перевернуть отсортированный список в обратном порядке.

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