{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Работа с файлами\n", "\n", "## Функция open, контекстный менеджер with\n", "\n", "Для работы с файлами существует функция [open](https://docs.python.org/3/library/functions.html#open), которая создаёт [файловый объект](https://docs.python.org/3/glossary.html#term-file-object) и наиболее часто используются с двумя параметрами:\n", "\n", "```python\n", "f = open(file, mode)\n", "```\n", "\n", "- file --- путь к файлу (глобальный или относительно текущей директории);\n", "- mode --- режим, в котором требуется открыть файл. \n", "Параметр `file` обязательный, а в качестве параметра `mode` передаётся строка символов:\n", "\n", "|Символ|Значение|\n", "| :--- | ---: |\n", "|`r`|Открыть на чтение (по умолчанию)|\n", "|`w`|Открыть на запись (содержимое существующего файла уничтожается) |\n", "|`x`|Создать файл и открыть на запись (ошибка, если файл существует) |\n", "|`a`|Открыть файл на запись (если файл существует, то дозаписывать в конец файла)|\n", "|`b`|Открыть как бинарный файл|\n", "|`t`|Открыть как текстовый файл (по умолчанию)|\n", "|`+`|Открыть на запись и чтение|\n", "\n", "По умолчанию функцию `open` открывает файл как текстовый для чтения, но указав `mode` можно это поменять.\n", "\n", "Чтобы продемонстрировать поведение функции `open`, рассмотрим пример. Для начала создадим файл средствами python. Сделать это в папке `tmp` можно было бы следующими командами.\n", "\n", "```python\n", "filename = \"./tmp/first_file.txt\"\n", "f = open(filename, \"w\")\n", "```\n", "\n", "Но у такого подхода есть один недостаток: он может не сработать на другой платформе. Тут есть ряд деталей\n", "- В `unix-like` системах (`Ubuntu`, `OSX`) в качестве разделителя каталогов используется символ `/`, а в `windows` используется `\\`;\n", "- Символ `\\` используется python для экранирования символов. Например, комбинация `\\n` --- новая строка и т.п. Чтобы корректно использовать разделитель каталогов `\\` в `windows` необходимо использовать или `r` строки (r`C:\\Users\\Ivan`) внутри которых `\\` не используется для экранирования, или экранировать сам символ `\\` ('C:\\\\\\Users\\\\\\Ivan');\n", "\n", "Поэтому, для работы с путями в файловых системах используется модуль стандартной библиотеки [os](https://docs.python.org/3/library/os.html) или [pathlib](https://docs.python.org/3/library/pathlib.html), которые предоставляют платформонезависимый интерфейс для работы с файловыми системами. Подробнее они будут обсуждаться ниже, а пока воспользуемся модулем `pathlib`, чтобы задать файл `first_file.txt` внутри папки `tmp`." ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tmp\\first_file.txt\n", "tmp\\first_file.txt\n" ] } ], "source": [ "from pathlib import Path\n", "import os\n", "\n", "# Используя pathlib\n", "folder = Path(\"tmp\")\n", "filename = folder / \"first_file.txt\"\n", "print(filename)\n", "\n", "# Используя os\n", "filename = os.path.join(\"tmp\", \"first_file.txt\")\n", "print(filename)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Как мы видим, создался путь в стиле `Windows`. Это произошло, потому-что сборка этих материалов осуществлялась на машине под управлением `Windows 10`. Теперь откроем файл на запись и запишем в него три строки. \n", "\n", "```{note}\n", "Так как мы хотим создать файл внутри папки `tmp`, то создадим её с помощью метода [os.makedirs](https://docs.python.org/3/library/os.html#os.makedirs).\n", "```" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ ".\n" ] } ], "source": [ "os.makedirs(folder, exist_ok=True)\n", "\n", "f = open(filename, \"w\")\n", "\n", "f.write(\"Первая строка.\\n\")\n", "f.write(\"Вторая строка.\\n\")\n", "f.write(\"Последняя строка.\")\n", "\n", "f.close()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "По исполнению этой ячейки должен был создаться файл `first_file.txt` в папке `./tmp`.\n", "\n", "Когда работа с файлом прекращена, следует вызвать метод `close()` у файлового объекта. Вообще говоря, этот метод вызовется автоматически при сборке мусора. Однако, если программа упадёт до закрытия файла, то могут произойти неожиданные последствия. \n", "\n", "Хорошей практикой является использование ключевого слова [with](https://docs.python.org/3/reference/compound_stmts.html#with) для работы с файлами:\n", "\n", "```python\n", "with open(filename, \"w\") as f:\n", " f.write(\"Первая строка.\\n\")\n", " f.write(\"Вторая строка.\\n\")\n", " f.write(\"Последняя строка.\")\n", "```\n", "\n", "Преимущество такого подхода заключается в том, что файл закроется автоматически при выходе за пределы блока `with ... as ...` или при возникновении исключения. \n", "\n", "\n", "Прочитаем содержимое только что созданного файла." ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Первая строка.\n", "Вторая строка.\n", "Последняя строка.\n", "\n" ] } ], "source": [ "with open(filename) as f:\n", " print(f.read())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```{note}\n", "При открытии на запись мы передали строку `\"w\"`, а т.к. открытие по умолчанию осуществляется на чтение, то можно не передавать строку `\"r\"`.\n", "```\n", "\n", "\n", "Метод `read` считывает содержимое всего файла в строку. Может удобнее быть читать из файла по строкам. Для этого можно рассмотреть три основных подхода:\n", "- `readline` считывает одну строку файла;\n", "- `readlines` возвращает список строк в файле;\n", "- использовать итератор файлового объекта; " ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "readline:\n", "Первая строка.\n", "\n", "Вторая строка.\n", "\n", "Последняя строка.\n", "\n", "||\n" ] } ], "source": [ "with open(filename) as f:\n", " print(\"readline:\")\n", " \n", " print(f.readline())\n", " print(f.readline())\n", " print(f.readline())\n", "\n", " end = f.readline()\n", " print(f\"|{end}|\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Метод `readline` считывает одну строку из файла и передвигает указатель на следующую. Если файл закончился, то считывается пустая строка." ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "readlines:\n", "['Первая строка.\\n', 'Вторая строка.\\n', 'Последняя строка.\\n']\n" ] } ], "source": [ "with open(filename) as f:\n", " print(\"readlines:\")\n", " print(f.readlines())\n", " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`readlines` считывает сразу все строки (начиная с текущего указателя) из файла и возвращает список из них. \n", "\n", "```{tip}\n", "Оба этих метода оставляют символы переноса в строке. Строковый метод [rstrip](https://docs.python.org/3/library/stdtypes.html#str.rstrip) позволяет избавиться от них при необходимости.\n", "```\n", "\n", "Самый распространенный подход --- использование файлового объекта для итерации по файлу." ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "'Первая строка.'\n", "'Вторая строка.'\n", "'Последняя строка.'\n" ] } ], "source": [ "with open(filename) as f:\n", " for line in f:\n", " print(repr(line.rstrip()))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Модули os, pathlib, glob\n", "\n", "При работе с файлами возникает необходимость взаимодействия с файловой системой. Чаще всего для этого используются 4 следующих модуля стандартной библиотеки:\n", "- [os](https://docs.python.org/3/library/os.html);\n", "- [pathlib](https://docs.python.org/3/library/pathlib.html);\n", "- [shutil](https://docs.python.org/3/library/shutil.html);\n", "- [glob](https://docs.python.org/3/library/glob.html);\n", "\n", "Разберем ряд методов из этих модулей.\n", "\n", "### Текущая директория\n", "\n", "Во время работы программы есть такое понятие как рабочая (текущая) директория/папка (похоже на `pwd` в `unix-like` системах). В самом начале исполнения программы рабочей директорией является та директория, из которой был запущен скрипт/ноутбук/сессия интерпретатора. Узнать эту папку в любой момент времени можно командой [os.getcwd](https://docs.python.org/3/library/os.html#os.getcwd), а поменять (перейти в другую папку, похоже на `cd` в командной строке) можно командой [os.chdir](https://docs.python.org/3/library/os.html#os.chdir) (сокращение от `change directory`).\n", "\n", "При открытии файлов путь к ним можно указывать в абсолютном формате (т.е. начиная с буквы диска в `windows` (`r\"C:\\users\\user_name\\some_file.txt\"`)\n", ") или с корневой директории `/` в `unix-like` системах (`\"/home/users/user_name/some_file.txt\"`)) или относительно текущей папки. \n", "\n", "### Путь к файлу/папке\n", "\n", "Из-за того, что разделители каталогов в разных операционных системах разные, то формирования пути рекомендуется использовать `os.path.join` или `Path` из `pathlib`.\n", "\n", "### Содержимое папки\n", "\n", "Получить список файлов и папок в директории можно: \n", "- методом [os.listdir](https://docs.python.org/3/library/os.html#os.listdir), который по умолчанию возвращает список файлов в текущей директории, но в качестве опционального аргумента принимает путь до папки, содержимое которой необходимо вывести; \n", "- методом [`glob.glob`](https://docs.python.org/3/library/glob.html#module-glob) из модуля `glob`. Этот метод предоставляет доступ к поиску файлов, название которых удовлетворят некоторому шаблону. Например, поиск всех файлов с расширением `\".txt\"` в текущей директории можно выполнить командой `glob.glob(\"*.txt\")`.\n", "\n", "Проверить, существует ли файл по указанному пути, можно методом [os.path.exists](https://docs.python.org/dev/library/os.path.html#os.path.exists).\n", "\n", "### Создание папок\n", "\n", "Для создания директорий используются методы \n", "- [os.mkdir](https://docs.python.org/3/library/os.html#os.mkdir) создаёт папку по указанному пути и бросает ошибку, если папка уже существует. Так же бросает ошибку, если по пути нужно создать больше, чем одну папку; Проверить, существует ли уже папка по указанному пути, можно методом [os.path.isdir](https://docs.python.org/dev/library/os.path.html#os.path.isdir);\n", "- [os.makedirs](https://docs.python.org/3/library/os.html#os.makedirs) делает тоже самое, но может обрабатывать существующие папки (параметр `exist_ok`) и создавать необходимые папки по пути.\n", "\n", "```{tip}\n", "Если нужно создавать временные файлы или папки исключительно на время работы программы или даже меньше, то модуль [tempfile](https://docs.python.org/3/library/tempfile.html) предоставляет удобный интерфейс для таких операций.\n", "```" ] } ], "metadata": { "interpreter": { "hash": "8617202e12f254480e1fae3258716b685f1a56bcbf234a446366b4fd3345ed22" }, "kernelspec": { "display_name": "Python 3.8.10 64-bit", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.10" } }, "nbformat": 4, "nbformat_minor": 5 }