Практические применения: работа с файлами и папками в ОС

С помощью короткой программы на питоне можно решить довольно широкий спектр задач которые до появления компьютеров просто не существовали. Это работа с файлами и папками операционной системы, работа с изображениями, mp3, pdf, электронной почтой, получение информации из сети интернет и много другое. Почти для всех задач, которые возникают, в сети можно найти подходящий рецепт на языке питон. На данный момент самым большим источником таких рецептов является сайт http://stackoverflow.com. Увы, он на английском, но у него есть и русская версия (в 1000 раз более бедная). Впрочем обычно не нужно много английского, чтобы понять, что происходит.

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

В этом листке мы будем пытаться научиться двум вещам: применять питон в практических задачах, и искать пути решения своих технических задач в интернете.

Если вы нашли рецепт, то следует убедиться, что рецепт на Python3, а не на Python2. Их легко отличить по print без скобок (такая конструкция в Python3 не работает). Зачастую print — это единственное, что нужно поправить. Но это, увы, не всегда так.

В каждой задаче нужно написать программу, которая решает поставленную проблему. В тестовую систему необходимо сдать текст программы, строчку-разделитель ####################, а затем ответ на задачу. Например, если задача — «Вычислить 2 в сотой степени», то возможный правильный ответ такой:

print(2 ** 100)
####################
1267650600228229401496703205376

A: Список файлов в папках

В архиве вы найдёте структуру папок с таксономией приматов (виды, семейства и т.д.). Где-то в глубине там даже есть человек разумный :). Необходимо вывести список из всех файлов, которые есть в папке Primates или её подпапках. Порядок файлов не важен.

Папку Primates из архива нужно положить куда-нибудь и скопировать путь к ней. Если путь имеет вид C:\Some\Directory\Users\Foo\Primates, то в начале программы рекомендуется сделать

os.chdir(r'C:\Some\Directory\Users\Foo')
После этого папка Primates окажется прямо в текущей рабочей папке программы.

Если бы стартовой была папка Primates\Haplorrhini\Simiiformes\Catarrhini\Hominoidea\Hominidae, то ответ был бы такой:

import os ... #################### Gorilla_beringei_beringei.txt Gorilla_beringei_graueri.txt Gorilla_gorilla_diehli.txt Gorilla_gorilla_gorilla.txt Gorilla_gorilla_uellensis.txt Homo_sapiens.txt Pan_paniscus.txt Pan_troglodytes_ellioti.txt Pan_troglodytes_schweinfurthii.txt Pan_troglodytes_troglodytes.txt Pan_troglodytes_vellerosus.txt Pan_troglodytes_verus.txt Pongo_abelii.txt Pongo_pygmaeus_pygmaeus.txt

B: Список файлов в папках с полными путями

В условиях задачи A выведите не только имена файлов, но и полные пути до них от начальной папки Primates.

Если бы стартовой была папка Primates\Haplorrhini\Simiiformes\Catarrhini\Hominoidea\Hominidae, то ответ был бы такой:

import os ... #################### Hominidae\Homininae\Gorilla\Gorilla_beringei\Gorilla_beringei_beringei.txt Hominidae\Homininae\Gorilla\Gorilla_beringei\Gorilla_beringei_graueri.txt Hominidae\Homininae\Gorilla\Gorilla_gorilla\Gorilla_gorilla_diehli.txt Hominidae\Homininae\Gorilla\Gorilla_gorilla\Gorilla_gorilla_gorilla.txt Hominidae\Homininae\Gorilla\Gorilla_gorilla\Gorilla_gorilla_uellensis.txt Hominidae\Homininae\Homo\Homo_sapiens.txt Hominidae\Homininae\Pan\Pan_paniscus.txt Hominidae\Homininae\Pan\Pan_troglodytes_ellioti.txt Hominidae\Homininae\Pan\Pan_troglodytes_schweinfurthii.txt Hominidae\Homininae\Pan\Pan_troglodytes_troglodytes.txt Hominidae\Homininae\Pan\Pan_troglodytes_vellerosus.txt Hominidae\Homininae\Pan\Pan_troglodytes_verus.txt Hominidae\Ponginae\Pongo\Pongo_abelii.txt Hominidae\Ponginae\Pongo\Pongo_pygmaeus\Pongo_pygmaeus_pygmaeus.txt

C: Прочитать все файлы в папке

В каждом файле из задачи A записан идентификатор этого таксона в открытом дереве жизни (Open Tree of Life). Выведите список этих идентификаторов.

Если бы стартовой была папка Primates\Haplorrhini\Simiiformes\Catarrhini\Hominoidea\Hominidae, то ответ был бы такой:

import os ... #################### ott624503 ott317556 ott835035 ott417953 ott620805 ott770315 ott158484 ott112831 ott752845 ott767875 ott84217 ott752847 ott770295 ott770308

D: Список всех подпапок

В условиях задачи A выведите список имён всех подпапок в папке Primates или её подпапках.

Если бы стартовой была папка Primates\Haplorrhini\Simiiformes\Catarrhini\Hominoidea\Hominidae, то ответ был бы такой:

import os ... #################### Homininae Ponginae Gorilla Homo Pan Gorilla_beringei Gorilla_gorilla Pongo Pongo_pygmaeus

E: Список папок с полными путями

В условиях задачи 04 выведите полные пути к папкам от Primates.

Если бы стартовой была папка Primates\Haplorrhini\Simiiformes\Catarrhini\Hominoidea\Hominidae, то ответ был бы такой:

import os ... #################### Hominidae\Homininae Hominidae\Ponginae Hominidae\Homininae\Gorilla Hominidae\Homininae\Homo Hominidae\Homininae\Pan Hominidae\Homininae\Gorilla\Gorilla_beringei Hominidae\Homininae\Gorilla\Gorilla_gorilla Hominidae\Ponginae\Pongo Hominidae\Ponginae\Pongo\Pongo_pygmaeus

Практическое задание: восстановление фотографий

В новой серии задач мы отрабатываем следующий сценарий: из-за резкого скачка электричества ваш компьютер вырубился во время записи файла на диск. В результате повреждена файловая система, поэтому уже невозможно найти свои файлы в "обычных" местах. На диске остались фотографии, которые вам дороги. С помощью специальной программы, которая сканирует весь диск и ищет последовательности байтов, похожие на фотографии, вы добыли всё, что можно. Так как файловая система умерла, то имён файлов и прочих атрибутов нет, есть только гора файлов с бессмысленными именами. Так как вы много работали с фотографиями, то там много повторов фотографий. А вот и архив с этими фотографиями. Для его открытия используйте 7z (брать отсюда).

План по наведению порядка:

Для того, чтобы каждый раз не клеить полный путь к имени файла, удобно перейти прямо в директорию к фотографиям при помощи
os.chdir('C:\\some\\path\\resc_photos')
Итак, поехали!

F: md5-хеши файлов

Для каждого файла из архива посчитайте его md5-хеш. Выведите все пары (ИМЯ_ФАЙЛА, ХЕШ). Порядок файлов неважен.

Если бы в папке были бы только файлы 0spuuzog.jpg, 1e1ztfom.jpg и 82t56870.jpg, то правильный ответ был бы таким:

import os import hashlib ... #################### 0spuuzog.jpg 2b04cbfba96acc2b20fb41a65c00116d 1e1ztfom.jpg a46e61e2cf9725d569ad755dbdd189c0 82t56870.jpg 2b04cbfba96acc2b20fb41a65c00116d

G: Отбор уникальных файлов

Отберите все уникальные фотографии и выведите их имена.

Если бы в папке были бы только файлы 0spuuzog.jpg, 1e1ztfom.jpg и 82t56870.jpg, то правильный ответ был бы таким:

import os import hashlib ... #################### 0spuuzog.jpg 1e1ztfom.jpg

H: Удаление лишнего

Получите список всех файлов, которые не лежат в списке из задачи G. Удалите все эти повторяющиеся файлы.

В тестовую систему необходимо сдать код программы, которая делает это, а также выводит список удаляемых файлов. Если бы в папке были бы только файлы 0spuuzog.jpg, 1e1ztfom.jpg и 82t56870.jpg, то правильный ответ был бы таким:

import os import hashlib ... #################### 82t56870.jpg

I: Дата съёмки

Известно, что фотографии сняты в августе 2015 года. В начале каждой фотографии находится EXIF — метаданные, например, дата-время съёмки, модель камеры и т.д. Теоретически их можно расшифровать, но сейчас в этом нет необходимости. Нужную дату можно найти и так. Откройте файл при помощи notepad++, и вы её увидите. После этого будет не сложно добыть дату для каждой фотографии автоматически. Здесь следует учитывать, что данные в файле не текстовые, а бинарные, поэтому открывать файл стоит так:

open(filename, 'rb')
Здесь r отвечает за чтение, а b — за то, что чтение бинарных данных. Бинарные строки очень похожи на обычные, только в коде перед ними стоит буква r. С ними можно делать почти все операции, что и с обычными текстовыми. Те символы бинарной строки, код которых не лежит в диапазоне acsii (от 32 до 127), выводятся как их код в 16-ричной системе счисления. Для получения из обычной текстовой строки бинарной используется метод encode:
>>> print('Привет, world!'.encode('utf-8'))
b'\xd0\x9f\xd1\x80\xd0\xb8\xd0\xb2\xd0\xb5\xd1\x82, world!'
Для обратного преобразования — метод decode:
>>> print(b'\xd0\x9f\xd1\x80\xd0\xb8\xd0\xb2\xd0\xb5\xd1\x82, world!'.decode('utf-8'))
Привет, world!

Выведите для каждого из оставшихся файлов дату и время съёмки в том виде, в котором они добываются из файла. Если бы в папке были бы только файлы 0spuuzog.jpg, 1e1ztfom.jpg и 82t56870.jpg, то правильный ответ был бы таким:

import os ... #################### 0spuuzog.jpg b'2015:08:23 14:26:12' 1e1ztfom.jpg b'2015:08:19 16:05:07'

J: Переименование

Теперь ничего не стоит переименовать файлы в имена вида "yyyy-mm-dd_hh-mm-ss.jpg". Ну, почти ничего, кроме того, что некоторые фотографии сняты в одну и ту же секунду. Если вдруг так окажется, то добавьте в конец имени файла перед ".jpg" один, два или три символа "_" (например, "2015-08-23_14-26-12_.jpg").

В тестовую систему необходимо сдать код программы, которая делает это, а также выводит имена новых файлов. Если бы в папке были бы только файлы 0spuuzog.jpg, 1e1ztfom.jpg и 82t56870.jpg, то правильный ответ был бы таким:

import os ... #################### 2015-08-23_14-26-12.jpg 2015-08-19_16-05-07.jpg

K: Чтение EXIF

Решение в предыдущих двух задачах не сработает, если у нас будет архив фотографий за 10 лет. Мы не сможем уверенно искать дату съёмки по строке "2015.08", ведь это может оказаться датой изменения, а не съёмки. Поэтому более правильное решение — расшифровать EXIF. Стандарт открыт, поэтому можно написать свою программу по его расшифровке. Однако это достаточно трудоёмко. Один из плюсов питона в том, что большинство подобных задач уже решены, и всё, что требуется, это найти подходящий пакет. В данном случае один из подходящих нам пакетов — это exifread.

Установка пакета exifread
Установка пакетов для Python в операционной системе Windows, увы, зачастую вызывает затруднения. Чтобы его установить, нужно выполнить в командной строке pip install exifread (win+R, затем cmd). Если у вас старая версия Python, не установлен pip, или при установке вы не поставили галочку "Добавить Python в PATH", то это не сработает. Тогда нужно скачать архив с пакетом, распаковать его, зайти внутрь папки exif-py-2.1.2, на пустом месте сделать клик правой кнопкой мыши вместе с нажатой клавишей Shift, выбрать "Открыть окно команд", и выполнить python setup.py install. Если и это не поможет, то нужно найти, где лежит исполняемый файл python.exe, и указать полный путь: "C:\Python34\python.exe" "Y:\exif-py-2.1.2\setup.py" install.

Если и это не помогло, то вы — неудачник установите свежую Minicond'у с Python 3.6, при установке не забудьте поставить галочку "Добавить в Path", и попробуйте всё сначала. Весьма вероятно, что вам заодно придётся обновить WingIDE.

Напишите программу, которая получает дату и время съёмки из EXIF для каждой фотографии при помощи модуля exifread. В тестовую систему необходимо сдать код программы, которая делает это, а также выводит полученные временные отметки. Если бы в папке были бы только файлы 0spuuzog.jpg, 1e1ztfom.jpg и 82t56870.jpg, то правильный ответ был бы таким:

import os import exifread ... #################### 2015:08:23 14:26:12 2015:08:19 16:05:07

L: Выбор объектива

При выборе объектива для фотоаппарата возникает вопрос: какие диапазон фокусных расстояний (углов поля зрения, см. картинку в википедии) вам необходим. Что выбирать, длиннофокусный объектив, широкоугольный объектив и т.д.? Довольно надёжный способ — посмотреть, какими фокусными расстояними вы пользуетесь.

Напишите программу, которая получает фокусное расстояние кадра из EXIF для каждой фотографии при помощи модуля exifread. Для каждого используемого фокусного расстояния выведите количество кадров (нужно отсортировать по увеличению фокусного расстояния). Постройте график количества кадров в зависимости от фокусного расстояния (нужно показать на экране). Учтите, что вам нужно эквивалентное фокусное расстояние, так как именно оно участвует в маркировке объективов.

В тестовую систему необходимо сдать код программы, которая делает это, а также выводит количества кадров. Если бы в папке были бы только файлы 0spuuzog.jpg, 1e1ztfom.jpg и 82t56870.jpg, то правильный ответ был бы таким:

import os import exifread ... #################### 24: 1 70: 1

M: Разложить по полочкам

После того, как мы «восстановили» имена файлов с фотографиями, появилась необходимость разложить их в отдельные папки в соответствии с поездками.

Будем тренироваться на «кошках». Внутри архива вы найдёте несколько файлов вида 2013-02-04_04-46-01.txt. Нужно разложить их по папкам вида yyyy/mm/dd, где yyyy, mm и dd — соответствующие части даты, а также переименовать: убрать из имени файла дату.

В тестовую систему необходимо сдать zip-архив, в котором находится папка crt_dirs с нужно структурой, а также py-файл с программой. Если бы архив из условия состоял лишь из файлов 2013-01-16_00-24-33.txt, 2013-01-16_16-40-26.txt и 2015-11-25_16-51-47.txt, то в правильная структура в zip файле ответа выглядела бы так:

crt_dirs/2013/01/16/00-24-33.txt crt_dirs/2013/01/16/16-40-26.txt crt_dirs/2015/11/25/16-40-26.txt crt_dirs/my_pgm.py