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

Функция open, контекстный менеджер with

Для работы с файлами существует функция open, которая создаёт файловый объект и наиболее часто используются с двумя параметрами:

f = open(file, mode)
  • file — путь к файлу (глобальный или относительно текущей директории);

  • mode — режим, в котором требуется открыть файл. Параметр file обязательный, а в качестве параметра mode передаётся строка символов:

Символ

Значение

r

Открыть на чтение (по умолчанию)

w

Открыть на запись (содержимое существующего файла уничтожается)

x

Создать файл и открыть на запись (ошибка, если файл существует)

a

Открыть файл на запись (если файл существует, то дозаписывать в конец файла)

b

Открыть как бинарный файл

t

Открыть как текстовый файл (по умолчанию)

+

Открыть на запись и чтение

По умолчанию функцию open открывает файл как текстовый для чтения, но указав mode можно это поменять.

Чтобы продемонстрировать поведение функции open, рассмотрим пример. Для начала создадим файл средствами python. Сделать это в папке tmp можно было бы следующими командами.

filename = "./tmp/first_file.txt"
f = open(filename, "w")

Но у такого подхода есть один недостаток: он может не сработать на другой платформе. Тут есть ряд деталей

  • В unix-like системах (Ubuntu, OSX) в качестве разделителя каталогов используется символ /, а в windows используется \;

  • Символ \ используется python для экранирования символов. Например, комбинация \n — новая строка и т.п. Чтобы корректно использовать разделитель каталогов \ в windows необходимо использовать или r строки (rC:\Users\Ivan) внутри которых \ не используется для экранирования, или экранировать сам символ \ (‘C:\Users\Ivan’);

Поэтому, для работы с путями в файловых системах используется модуль стандартной библиотеки os или pathlib, которые предоставляют платформонезависимый интерфейс для работы с файловыми системами. Подробнее они будут обсуждаться ниже, а пока воспользуемся модулем pathlib, чтобы задать файл first_file.txt внутри папки tmp.

from pathlib import Path
import os

# Используя pathlib
folder = Path("tmp")
filename = folder / "first_file.txt"
print(filename)

# Используя os
filename = os.path.join("tmp", "first_file.txt")
print(filename)
tmp\first_file.txt
tmp\first_file.txt

Как мы видим, создался путь в стиле Windows. Это произошло, потому-что сборка этих материалов осуществлялась на машине под управлением Windows 10. Теперь откроем файл на запись и запишем в него три строки.

Note

Так как мы хотим создать файл внутри папки tmp, то создадим её с помощью метода os.makedirs.

os.makedirs(folder, exist_ok=True)

f = open(filename, "w")

f.write("Первая строка.\n")
f.write("Вторая строка.\n")
f.write("Последняя строка.")

f.close()

По исполнению этой ячейки должен был создаться файл first_file.txt в папке ./tmp.

Когда работа с файлом прекращена, следует вызвать метод close() у файлового объекта. Вообще говоря, этот метод вызовется автоматически при сборке мусора. Однако, если программа упадёт до закрытия файла, то могут произойти неожиданные последствия.

Хорошей практикой является использование ключевого слова with для работы с файлами:

with open(filename, "w") as f:
    f.write("Первая строка.\n")
    f.write("Вторая строка.\n")
    f.write("Последняя строка.")

Преимущество такого подхода заключается в том, что файл закроется автоматически при выходе за пределы блока with ... as ... или при возникновении исключения.

Прочитаем содержимое только что созданного файла.

with open(filename) as f:
    print(f.read())
Первая строка.
Вторая строка.
Последняя строка.

Note

При открытии на запись мы передали строку "w", а т.к. открытие по умолчанию осуществляется на чтение, то можно не передавать строку "r".

Метод read считывает содержимое всего файла в строку. Может удобнее быть читать из файла по строкам. Для этого можно рассмотреть три основных подхода:

  • readline считывает одну строку файла;

  • readlines возвращает список строк в файле;

  • использовать итератор файлового объекта;

with open(filename) as f:
    print("readline:")
    
    print(f.readline())
    print(f.readline())
    print(f.readline())

    end = f.readline()
    print(f"|{end}|")
readline:
Первая строка.

Вторая строка.

Последняя строка.
||

Метод readline считывает одну строку из файла и передвигает указатель на следующую. Если файл закончился, то считывается пустая строка.

with open(filename) as f:
    print("readlines:")
    print(f.readlines())
    
readlines:
['Первая строка.\n', 'Вторая строка.\n', 'Последняя строка.']

readlines считывает сразу все строки (начиная с текущего указателя) из файла и возвращает список из них.

Tip

Оба этих метода оставляют символы переноса в строке. Строковый метод rstrip позволяет избавиться от них при необходимости.

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

with open(filename) as f:
    for line in f:
        print(repr(line.rstrip()))
'Первая строка.'
'Вторая строка.'
'Последняя строка.'

Модули os, pathlib, glob

При работе с файлами возникает необходимость взаимодействия с файловой системой. Чаще всего для этого используются 4 следующих модуля стандартной библиотеки:

Разберем ряд методов из этих модулей.

Текущая директория

Во время работы программы есть такое понятие как рабочая (текущая) директория/папка (похоже на pwd в unix-like системах). В самом начале исполнения программы рабочей директорией является та директория, из которой был запущен скрипт/ноутбук/сессия интерпретатора. Узнать эту папку в любой момент времени можно командой os.getcwd, а поменять (перейти в другую папку, похоже на cd в командной строке) можно командой os.chdir (сокращение от change directory).

При открытии файлов путь к ним можно указывать в абсолютном формате (т.е. начиная с буквы диска в windows (r"C:\users\user_name\some_file.txt") ) или с корневой директории / в unix-like системах ("/home/users/user_name/some_file.txt")) или относительно текущей папки.

Путь к файлу/папке

Из-за того, что разделители каталогов в разных операционных системах разные, то формирования пути рекомендуется использовать os.path.join или Path из pathlib.

Содержимое папки

Получить список файлов и папок в директории можно:

  • методом os.listdir, который по умолчанию возвращает список файлов в текущей директории, но в качестве опционального аргумента принимает путь до папки, содержимое которой необходимо вывести;

  • методом glob.glob из модуля glob. Этот метод предоставляет доступ к поиску файлов, название которых удовлетворят некоторому шаблону. Например, поиск всех файлов с расширением ".txt" в текущей директории можно выполнить командой glob.glob("*.txt").

Проверить, существует ли файл по указанному пути, можно методом os.path.exists.

Создание папок

Для создания директорий используются методы

  • os.mkdir создаёт папку по указанному пути и бросает ошибку, если папка уже существует. Так же бросает ошибку, если по пути нужно создать больше, чем одну папку; Проверить, существует ли уже папка по указанному пути, можно методом os.path.isdir;

  • os.makedirs делает тоже самое, но может обрабатывать существующие папки (параметр exist_ok) и создавать необходимые папки по пути.

Tip

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