Какой бы проект вы ни разрабатывали, вам не избежать работы с файлами либо на компьютере, либо на сервере. И неудивительно, поскольку они являются самыми распространёнными контейнерами для хранения взаимосвязанной и обычно структурированной информации. При этом многим приходится выискивать конкретные операции, связанные с обработкой файлов. Поэтому мы решили посвятить данную статью 10 наиболее эффективным техникам для работы с файлами в Python.
1. Показ текущей директории
Чтобы узнать текущую рабочую директорию, мы можем просто ввести функцию getcwd()
модуля os, как показано ниже:
>>> # Узнаём текущую рабочую директорию
... import os
... print("Current Work Directory:", os.getcwd())
...
Current Work Directory: /Users/ycui1/PycharmProjects/Medium_Python_Tutorials
>>> # В качестве альтернативы можно использовать pathlib
... from pathlib import Path
... print("Current Work Directory:", Path.cwd())
...
Current Work Directory: /Users/ycui1/PycharmProjects/Medium_Python_Tutorials
Данный код также демонстрирует возможность использования модуля pathlib
для получения текущей рабочей директории. Обратите внимание, что для выполнения операций с файлами именно этот модуль является предпочтительным вариантом, и в статье вас ждёт немало примеров его употребления. Однако, если у вас устаревшая версия Python (< 3.4), то вам придётся использовать модуль os
.
2. Создание новой директории
Для создания новой директории можно применить функцию mkdir()
, которая выполнит эту операцию по конкретно заданному пути. Если вы просто укажете имя новой директории, то она будет создана в текущем каталоге:
>>> # Создаём новую директорию в текущей
... os.mkdir("test_folder")
... print("Is the directory there:", os.path.exists("test_folder"))
...
Is the directory there: True
>>> # Создаём новую директорию в конкретно заданном каталоге
... os.mkdir('/Users/ycui1/PycharmProjects/tmp_folder')
... print("Is the directory there:", os.path.exists('/Users/ycui1/PycharmProjects/tmp_folder'))
...
Is the directory there: True
Однако, если вы намерены создать новую директорию с несколькими вложенными уровнями (имеется в виду наличие одной папки внутри другой), то вам необходимо использовать функцию makedirs()
. Обратимся к простому примеру:
>>> # Создаём новую директорию с поддиректориями
... os.makedirs('tmp_level0/tmp_level1')
... print("Is the directory there:", os.path.exists("tmp_level0/tmp_level1"))
...
Is the directory there: True
Если же у вас последние версии Python (≥ 3.4), то для решения вышеуказанной задачи можно воспользоваться преимуществом модуля pathlib
. При этом он способен не только создавать поддиректории, но также при необходимости работать с каталогами, отсутствующими в пути. Рассмотрим пример:
# Используем pathlib
from pathlib import Path
Path("test_folder").mkdir(parents=True, exist_ok=True)
Имейте в виду, что попытка повторного выполнения вышеприведённого кода может вызвать проблемы — вы не сможете создать новую директорию, если такая уже существует. Стоит отметить, что эта проблема решается путём присвоения аргументу exist_ok
значения True
, как показано выше. А вот значение False
, установленное для него по умолчанию, не позволит повторно создать уже существующую директорию и приведёт к ошибке.
>>> # Используем pathlib
... from pathlib import Path
... Path("test_folder").mkdir(parents=True, exist_ok=False)
...
Traceback (most recent call last):
File "<input>", line 3, in <module>
File "/Users/ycui1/.conda/envs/Medium/lib/python3.8/pathlib.py", line 1284, in mkdir
self._accessor.mkdir(self, mode)
FileExistsError: [Errno 17] File exists: 'test_folder'
3. Удаление директорий и файлов
По завершении операций с файлами или папками, возможно, потребуется их удалить, чтобы упорядочить ресурсы компьютера. Для удаления файла в модуле os применяется функция remove()
, а для удаления папки — функция rmdir()
. Попытка же удалить директорию с помощью remove()
вызовет ошибку. Рассмотрим применение этих функций:
>>> # Удаление файла
... print(f"* Before deleting file {os.path.isfile('tmp.txt')}")
... os.remove('tmp.txt')
... print(f"* After deleting file {os.path.exists('tmp.txt')}")
...
* Before deleting file True
* After deleting file False
>>> # Удаление директории
... print(f"* Before deleting directory {os.path.isdir('tmp_folder')}")
... os.rmdir('tmp_folder')
... print(f"* After deleting directory {os.path.exists('tmp_folder')}")
...
* Before deleting directory True
* After deleting directory False
При использовании модуля pathlib за удаление файла отвечает метод unlink()
, а за удаление директории — rmdir()
. Обратите внимание, что они оба являются методами экземпляра объекта Path
.
4. Получение списка файлов
В процессе обработки данных для аналитики или проектов МО вам потребуется получить список файлов в определённой директории. Зачастую их имена соответствуют определённому шаблону. Допустим, мы хотим найти все файлы .txt в директории. Далее рассмотрим, как это можно сделать с помощью метода glob()
с объектом Path
. Обратите внимание, что данный метод создаёт генератор с возможностью итерации. Следующий код наглядно демонстрирует создание генератором списка путей файлов:
>>> txt_files = list(Path('.').glob("*.txt"))
... print("Txt files:", txt_files)
...
Txt files: [PosixPath('hello_world.txt'), PosixPath('hello.txt')]
Как вариант, также удобно использовать модуль glob напрямую, как показано ниже. Он располагает аналогичной функциональностью, создавая списки имён файлов, с которыми впоследствии можно работать. Заметьте, что Path.glob()
создаёт пути. Оба метода будут работать в большинстве сценариев, таких как чтение и запись файлов.
>>> from glob import glob
... files = list(glob('h*'))
... print("Files starting with h:", files)
...
Files starting with h: ['hello_world.txt', 'hello.txt']
5. Перемещение и копирование файлов
Перемещение и копирование — одна из стандартных задач управления файлами, которая довольно легко решается в Python. Для перемещения вы просто переименовываете файл, заменяя его старую директорию целевой. Предположим, необходимо переместить все файлы .txt в другую папку. В следующем примере кода мы увидим, как это можно сделать с помощью модуля pathlib:
>>> target_folder = Path("target_folder")
... target_folder.mkdir(parents=True,exist_ok=True)
... source_folder = Path('.')
...
... txt_files = source_folder.glob('*.txt')
... for txt_file in txt_files:
... filename = txt_file.name
... target_path = target_folder.joinpath(filename)
... print(f"** Moving file {filename}")
... print("Target File Exists:", target_path.exists())
... txt_file.rename(target_path)
... print("Target File Exists:", target_path.exists(), '\n')
...
** Moving file hello_world.txt
Target File Exists: False
Target File Exists: True
** Moving file hello.txt
Target File Exists: False
Target File Exists: True
Копирование же можно выполнить при помощи функциональности, доступной в shutil, ещё одном полезном модуле из стандартной библиотеки для операций с файлами. Здесь за это отвечает функция copy()
, в которой исходный и целевой файлы указываются в виде строк. Ниже вы увидите простой пример. Конечно, вы можете объединить функции copy()
и glob()
для работы с группой файлов, соответствующих одному паттерну.
>>> import shutil
...
... source_file = "target_folder/hello.txt"
... target_file = "hello2.txt"
... target_file_path = Path(target_file)
... print("* Before copying, file exists:", target_file_path.exists())
... shutil.copy(source_file, target_file)
... print("* After copying, file exists:", target_file_path.exists())
...
* Before copying, file exists: False
* After copying, file exists: True
6. Проверка директории/файла
На самом деле, эта операция уже много раз встречалась в вышеприведённых примерах. В них для проверки того, существует ли конкретный путь, применялся метод exists()
. При условии положительного ответа он возвращает True
, в противном случае — False
. Примечательно, что эта функция доступна в обоих модулях, os и pathlib, но с разными сигнатурами. Рассмотрим соответствующие примеры их применения:
# exists() в модуле os
os.path.exists('path_to_check')
# exists() в модуле pathlib
Path('directory_path').exists()
В модуле pathlib можно также проверить, является ли путь директорией или файлом с готовыми к вызову функциями. Обратимся к следующему примеру:
# Проверяем, является ли путь директорией
os.path.isdir('path_to_check')
Path('path_to_check').is_dir()
# Проверяем, является ли путь файлом
os.path.isfile('path_to_check')
Path('path_to_check').is_file()
7. Получение информации о файле
При работе с файлами во многих сценариях возникает необходимость извлечения их имён. С объектом Path
это просто как дважды два, и вы уже были свидетелями его применения. Можно просто извлечь атрибут name
файлового объекта Path
. Если же вам нужно узнать только имя без расширения, то извлекать следует атрибут stem
. Следующий фрагмент кода демонстрирует соответствующие случаи применения:
for py_file in Path().glob('c*.py'):
... print('Name with extension:', py_file.name)
... print('Name only:', py_file.stem)
...
Name with extension: closures.py
Name only: closures
Name with extension: counter.py
Name only: counter
Name with extension: context_management.py
Name only: context_management
В отдельных случаях вам потребуется узнать расширение файла, с которым вы работаете. Чаще всего можно воспользоваться атрибутом suffix
файлового объекта Path
, как показано ниже:
>>> file_path = Path('closures.py')
... print("File Extension:", file_path.suffix)
...
File Extension: .py
Если необходимо получить больше информации о файле, например, его размер и время изменения, то для этого в нашем распоряжении есть метод stat()
, принцип действия которого аналогичен os.stat()
, знакомого тем, кто привык работать с модулем os.
>>> # Получаем объект st_stat из пути
... current_file_path = Path('iterable_usages.py')
... file_stat = current_file_path.stat()
...
>>> # Получаем информацию о размере файла:
... print("File Size in Bytes:", file_stat.st_size)
File Size in Bytes: 3531
>>> # Получаем информацию о времени последнего обращения
... print("When Most Recent Access:", file_stat.st_atime)
When Most Recent Access: 1595435202.310935
>>> # Получаем информацию о времени последнего изменения содержимого
... print("When Most Recent Modification:", file_stat.st_mtime)
When Most Recent Modification: 1594127561.3204417
8. Чтение файлов
Одна из важнейших операций с файлами — считывание их данных. В конце концов, содержимое файла является, вероятно, единственной причиной его появления. Самый традиционный способ состоит в создании файлового объекта с помощью встроенной функции open()
. По умолчанию она откроет файл в режиме чтения и будет работать с его данными как с текстом. Рассмотрим пример:
>>> # Чтение всех текстов
... with open("hello2.txt", 'r') as file:
... print(file.read())
...
Hello World!
Hello Python!
>>> # Чтение построчно
... with open("hello2.txt", 'r') as file:
... for i, line in enumerate(file, 1):
... print(f"* Reading line #{i}: {line}")
...
* Reading line #1: Hello World!
* Reading line #2: Hello Python!
Этот код демонстрирует самые распространённые способы чтения содержимого. Если вы знаете, что ваш файл включает немного данных, можете считать их все за раз с помощью метода read()
. Но если он очень крупный, то следует рассмотреть вариант с генератором, роль которого выполняет файловый объект. Он обрабатывает данные построчно и тем самым экономно расходует память, обходясь без загрузки всех данных при применении read()
.
Как уже ранее упоминалось, функция open()
по умолчанию работает с содержимым файла как с текстом. Однако в случае с бинарными файлами необходимо явно задать данное условие. Например, вместо ‘r’
следует ввести ‘rb’
. Это требование также относится и к записи файлов, о чём мы поговорим далее. Ещё один непростой момент связан с кодировкой файла. Во многих случаях функция open()
сможет выполнить эту операцию за нас, и большинство файлов, с которыми мы работаем, будут в кодировке “utf-8”. Если же вы обрабатываете файлы, применяя другие форматы кодировки, вам следует установить аргумент encoding
.
9. Запись файлов
Для записи данных можно опять же создать файловый объект, открыв файл в режиме записи (‘w’
) или дозаписи (‘a’
). В первом случае при записи данных в файл его старое содержимое удаляется, а во втором — данные добавляются в конец файла. Рассмотрим пример работы этих двух режимов в следующем фрагменте кода.
>>> # Запись новых данных в файл
... with open("hello3.txt", 'w') as file:
... text_to_write = "Hello Files From Writing"
... file.write(text_to_write)
...
>>> # Добавление данных
... with open("hello3.txt", 'a') as file:
... text_to_write = "\nHello Files From Appending"
... file.write(text_to_write)
...
>>> # Проверяем, содержит ли файл правильные данные
... with open("hello3.txt") as file:
... print(file.read())
...
Hello Files From Writing
Hello Files From Appending
Этот код подтверждает, что мы можем записывать данные в двух режимах: записи и дозаписи. Обратили вы внимание или нет, но каждый раз при открытии файла использовалась инструкция with
. Объясняется это тем, что она создаёт контекст для обработки файла и помогает закрыть файловый объект по завершении операций. Если же вовремя этого не сделать, то открытый файловый объект может повредиться.
10. Архивирование и разархивирование файлов
При работе с большим числом файлов может потребоваться их архивирование для долгосрочного хранения или временной передачи. Соответствующие возможности предоставляются модулем zipfile. Для архивирования файлов функцией ZipFile()
создаётся файловый объект zip, что напоминает случай с функцией open()
, поскольку обе эти функции предусматривают создание файлового объекта, управляемого контекстным менеджером (вспоминаете применение инструкции with
?). Обратимся к фрагменту кода с простым примером:
>>> from zipfile import ZipFile
...
... # Создание zip-файла, содержащего все текстовые файлы в директории
... with ZipFile('text_files.zip', 'w') as file:
... for txt_file in Path().glob('*.txt'):
... print(f"*Add file: {txt_file.name} to the zip file")
... file.write(txt_file)
...
*Add file: hello3.txt to the zip file
*Add file: hello2.txt to the zip file
Вы можете получить zip-файл из внешнего источника, и вам потребуется извлечь из него файлы. Чтобы не усложнять, допустим, что мы распаковываем их в текущую директорию. Обратите внимание на то, что имена файлов в zip-файле совпадают с содержащимися в директории, вследствие чего последние будут перезаписаны без предупреждения. Поэтому вам следует рассмотреть вариант извлечения содержимого zip-файла в отдельную папку, где такой проблемы перезаписи не возникнет.
>>> # Для разархивирования только что созданного файла
... with ZipFile('text_files.zip') as zip_file:
... zip_file.printdir()
... zip_file.extractall()
...
File Name Modified Size
hello3.txt 2020-07-30 20:29:50 51
hello2.txt 2020-07-30 18:29:52 26
Заключение
Итак, в данной статье мы рассмотрели 10 наиболее полезных операций по работе с файлами. Как вы могли убедиться, все они укладываются в несколько строк кода, поэтому не представляют абсолютно никакой сложности. Если вы с ними не знакомы, то надеемся, что эта статья послужит для вас кратким руководством в процессе работы с файлами.
Читайте также:
- Суть 4 хитроумных концепций Python для новичков
- 10 идиоматических приемов для эффективного программирования на Python
- 7 Способов вывести свои новые навыки Python на следующий уровень
Читайте нас в Telegram, VK и Яндекс.Дзен
Перевод статьи Yong Cui, Ph.D.: The Top 10 File Handling Techniques in Python