Работа с файлами в Python

Многим из нас знакома ситуация, когда компьютер оказывался завален тоннами беспорядочных файлов. Только что вы открывали большой zip-архив, спустя мгновение – файлы повсюду в этой директории, вперемешку с важными документами. Наверняка приходилось мучительно скучно сортировать эту свалку вручную? Чтобы облегчить подобные задачи, мы сейчас погрузимся в «умную» работу с файлами при помощи Python.

«Работай умнее, а не усерднее». Карл Баркс

Итак, приступим, вооружившись Python версии 3.4 или выше. Сначала пройдемся по модулю OS, а по ходу дела познакомимся еще с несколькими. Всё, что мы будем использовать, доступно в Python «с коробки», так что ничего дополнительно устанавливать не потребуется.

Генератор случайных файлов

Создадим папку ManageFiles, а внутри нее еще одну — RandomFiles. Дерево каталогов теперь должно выглядеть вот так:

ManageFiles/
 |
 |_RandomFiles/

Чтобы поиграться с файлами, мы сгенерируем их случайным образом в директории RandomFiles. Создайте файл create_random_files.py в папке ManageFiles. Вот что должно получиться:

ManageFiles/
 |
 |_ create_random_files.py
 |_RandomFiles/

Готово? Теперь поместите в файл следующий код, и перейдем к его рассмотрению:

import os
from pathlib import Path
import random

list_of_extensions = ['.rst', '.txt', '.md', '.docx', '.odt', '.html', '.ppt', '.doc']

# перейти в папку RandomFiles
os.chdir('./RandomFiles')

for item in list_of_extensions:
    # создать 20 случайных файлов для каждого расширения имени
    for num in range(20):
        # пусть имя файла начинается со случайного числа от 1 до 50
        file_name = random.randint(1, 50)
        file_to_create = str(file_name) + item
        Path(file_to_create).touch()

Начиная с Python 3.4 мы получили pathlib, нашу маленькую волшебную палочку. Также мы импортируем функцию random для генерации случайных чисел, но ее мы посмотрим в действии чуть ниже.

Сперва создадим список файловых расширений для формирования названий файлов. Не стесняйтесь добавить туда свои варианты.

Далее мы переходим в папку RandomFiles и запускаем цикл. В нем мы просто говорим: возьми каждый элемент list_of_extensions и сделай с ним кое-что во внутреннем цикле 20 раз.

Теперь пришло время для импортированной функции random. Используем ее для производства случайных чисел от 1 до 50. Это просто не очень творческий способ побыстрее дать названия нашим тестовым файлам: к сгенерированному числу добавим расширение файла и получим что-то вроде 23.txt или 14.txt. И так 20 раз для каждого расширения. В итоге образуется беспорядок, достаточный для того, чтобы его было лень сортировать вручную.

Итак, запустим наш генератор хаоса через терминал.

python create_random_files.py

Поздравляю, теперь у нас полная папка неразберихи. Будем распутывать.

В той же директории, где create_random_files.py, создадим файл clean_up.py и поместим туда следующий код.

Способ 1

import os
import shutil
import glob

# перейти в папку RandomFiles
os.chdir('./RandomFiles')

# получить список файлов в папке RandomFiles
files_to_group = []
for random_file in os.listdir('.'):
    files_to_group.append(random_file)

# получить все расширения имен всех файлов
file_extensions = []
for our_file in files_to_group:
    file_extensions.append(os.path.splitext(our_file)[1])

print(set(file_extensions))

file_types = set(file_extensions)

for type in file_types:
    new_directory = type.replace(".", " ")
    os.mkdir(new_directory)  # создать папку с именем данного расширения

    for fname in glob.glob(f'*.{type[1:]}'):
        shutil.move(fname, new_directory)

Для этого импортируем еще две библиотеки: shutil и glob. Первая поможет перемещать файлы, а вторая – находить и систематизировать. Но обо всем по порядку.

Для начала получим список всех файлов в директории.

Здесь мы предполагаем, что у нас нет ни малейшего понятия о том, какие именно файлы лежат в этой папке. Вместо того, чтобы вписывать все расширения вручную и использовать лестницу инструкций if или switch, мы желаем, чтобы программа сама просмотрела каталог и определила, на какие типы можно разделить его содержание. Что, если бы там были файлы с десятками расширений или логи? Вы бы стали описывать их вручную?

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

Обратите внимание на разделение строки:

os.path.splitext(our_file)[1]

Сейчас наша переменная our_file выглядит как-нибудь так: 5.docx. Когда разделим ее, получим следующее:

`('5', '.docx')`

Мы возьмем отсюда второй элемент по индексу [1], то есть .docx. Ведь по индексу [0] у нас располагается 5.

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

# создать множество и присвоить его переменной
file_types = set(file_extensions)

Заметим, что в списке типов файлов каждое расширение содержит . в начале. Если мы назовем так папки на UNIX-системе, то они будут скрытыми, что не входит в наши намерения.

Поэтому, итерируя по нашему множеству, мы заменяем точку на пустую строку. И создаем папку с полученным названием.

new_directory = type.replace(".", " ")
# наша директория теперь будет называться "docx"

Но чтобы переместить файлы, нам все еще нужно расширение .docx.

for fname in glob.glob(f'*.{type[1:]}')

Этим попросту отбираем все файлы, оканчивающиеся расширением .docx. Заметьте, что в f'*.{type[1:]}') нет пробелов.

Символ подстановки * обозначает, что подходит любое имя, если оно заканчивается на .docx. Поскольку мы уже включили точку в поиск, мы используем [1:], что значит «все после первого символа». В нашем примере это docx.

Что дальше? Перемещаем любые файлы с данным расширением в директорию с тем же названием.

shutil.move(fname, new_directory)

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

Способ 2

В качестве хитрого способа создать список в одну строку можно использовать генераторы.

import os
import shutil
import glob

# перейти в папаку RandomFiles
os.chdir('./RandomFiles')
# добавить все файлы в данной папке в список
all_files = [x for x in os.listdir('.')]

# создать множество расширений имен файлов в этой папке
file_types = set((os.path.splitext(f)[1] for f in all_files))

for ftype in file_types:
    new_directory = ftype.replace(".", '')
    os.mkdir(new_directory)

    for fname in glob.glob(f'*.{ftype[1:]}'):
        shutil.move(fname, new_directory)

Оба варианта сработают, и все ваши файлы будут отсортированы по расширению.

ManageFiles/
 |
 |_create_random_files.py
 |_RandomFiles/
    |_doc
    |_docx
    |_html
    |_md
    |_odt
    |_ppt

Вот и все. Если вам когда-либо понадобится отсортировать файлы таким образом, вы сэкономите немало времени 😊. Код упражнения доступен здесь.