{ "cells": [ { "cell_type": "markdown", "id": "864255d1-0a87-4d23-947f-895766366ac8", "metadata": {}, "source": [ "# Обработка данных. Часть 2" ] }, { "cell_type": "markdown", "id": "85765da4-3d99-422e-96ba-592fbdfd5f10", "metadata": {}, "source": [ "## Работа с файловой системой\n", "\n", "### Пути к файлам\n", "\n", "В Windows пути:\n", "* Начинаются с имени диска (`C:`, `D:`, …)\n", "* Регистронезависимы (`Hello.txt` и `hello.txt` один файл)\n", "* Компоненты пути разделяются символом `\\`\n", "\n", "В POSIX системах пути:\n", "* Начинаются с корневой директории `/`\n", "* Регистрозависимы (`Hello.txt` и `hello.txt` разные файлы)\n", "* Компоненты пути разделяются символом `/`\n", "\n", "Пути бывают:\n", "* Абсолютные (`C:\\python\\python.exe`, `/usr/bin/python`)\n", "* Относительные (`..\\python.exe`, `../python`)\n", "* Со специальными сокращениями `%WINDIR%\\notepad.exe`, `~/file.txt`\n", "\n", "### Работа с путями как со строками\n", "\n", "Пути к файлам с точки зрения системы это текстовые строки.\n" ] }, { "cell_type": "code", "execution_count": 1, "id": "e678e9db-fd1e-4c5e-8e78-4e9de51ad22e", "metadata": {}, "outputs": [], "source": [ "filepath = r\"C:\\Windows\\notepad.exe\"\n", "filepath = \"/usr/bin/nano\"" ] }, { "cell_type": "markdown", "id": "5cf4235a-cfd5-408a-8802-77b3b8f61a53", "metadata": {}, "source": [ "* Просто\n", "* Всегда работает\n", "* Неперенасимо\n", "\n", "### Работа с путями как с объектами" ] }, { "cell_type": "code", "execution_count": 6, "id": "85f3630a-e51a-4d22-be4b-52f7e2bb0d25", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "('/', 'usr', 'lib', 'libm.a')\n", "/usr/lib\n", "/usr\n" ] } ], "source": [ "from pathlib import Path\n", "p = Path(\"/usr/lib/libm.a\")\n", "print(p.parts)\n", "print(p.parents[0])\n", "print(p.parents[1])" ] }, { "cell_type": "markdown", "id": "62cfc7e0-81b4-46bc-afa0-f0fb56112c12", "metadata": {}, "source": [ "* Сложнее\n", "* Иногда приходится приводить к строкам\n", "* Переносимо\n", "\n", "### Некоторые функции\n", "\n", "Основные модули для работы с путями:" ] }, { "cell_type": "code", "execution_count": 8, "id": "432b1c5a-906e-4adc-88fb-89315ee6d914", "metadata": {}, "outputs": [], "source": [ "import os\n", "import os.path\n", "import shutil" ] }, { "cell_type": "markdown", "id": "c1d95d15-044a-4a77-8167-d4a80485272e", "metadata": {}, "source": [ "#### Текущая папка скрипта" ] }, { "cell_type": "code", "execution_count": 11, "id": "f91c97a7-58e8-4a57-8c39-0ee78cc325fe", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "/usr/lib\n" ] } ], "source": [ "os.chdir(\"/usr/lib\")\n", "print(os.getcwd())" ] }, { "cell_type": "markdown", "id": "7fc3bb52-f41c-4aa0-9b36-b005528adcdb", "metadata": {}, "source": [ "#### Пути абсолютные и относительные" ] }, { "cell_type": "code", "execution_count": 12, "id": "50af9911-6c61-4ef0-9249-f9e31aaa7820", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "libm.a\n", "../bin/bash\n", "/usr/bin/bash\n" ] } ], "source": [ "p1 = os.path.relpath(\"/usr/lib/libm.a\")\n", "print(p1)\n", "p2 = os.path.relpath(\"/usr/bin/bash\")\n", "print(p2)\n", "p3 = os.path.abspath('../../usr/bin/bash')\n", "print(p3)" ] }, { "cell_type": "markdown", "id": "44068c12-db31-43b3-ad5d-fd42d0515b2e", "metadata": {}, "source": [ "#### Компоненты пути" ] }, { "cell_type": "code", "execution_count": 13, "id": "3c47bf67-5513-4ae0-8785-ec71e5a7a53b", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "libm.a\n", "/usr/lib\n", "['', 'usr', 'lib', 'libm.a']\n", ".a\n" ] } ], "source": [ "# Имя файла без пути\n", "p4 = os.path.basename(\"/usr/lib/libm.a\")\n", "print(p4)\n", "\n", "# Путь без имени\n", "p5 = os.path.dirname(\"/usr/lib/libm.a\")\n", "print(p5)\n", "\n", "# Компоненты пути\n", "p6 = os.path.normpath(\"/usr/lib/libm.a\").split(os.sep)\n", "print(p6)\n", "\n", "# Расширение файла (последнее)\n", "_, p7 = os.path.splitext(\"/usr/lib/libm.a\")\n", "print(p7)" ] }, { "cell_type": "markdown", "id": "7ac08cd1-b72e-4a84-8875-01d57021bb45", "metadata": {}, "source": [ "#### Существование файлов и папок" ] }, { "cell_type": "code", "execution_count": 14, "id": "b09e94e2-e9f1-43d3-b7c4-e7c58bb5e007", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "True\n", "True\n" ] } ], "source": [ "# Существование файла\n", "s1 = os.path.isfile(\"/usr/lib/libm.a\")\n", "print(s1)\n", "\n", "# Существование папки\n", "s2 = os.path.isdir(\"/usr/lib\")\n", "print(s1)" ] }, { "cell_type": "markdown", "id": "3db3c252-9abb-4d46-9bd4-dc36201f1554", "metadata": {}, "source": [ "#### Атрибуты файлов и папок" ] }, { "cell_type": "code", "execution_count": 16, "id": "93679bc6-9a28-4e44-954a-b6f75b60ad9a", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1724592400.795275\n", "1722888052.0\n", "98\n" ] } ], "source": [ "# Время создания\n", "t2 = os.path.getctime(\"/usr/lib/libm.a\")\n", "print(t2)\n", "\n", "# Время изменения\n", "t3 = os.path.getmtime(\"/usr/lib/libm.a\")\n", "print(t3)\n", "\n", "# Размер в байтах\n", "s = os.path.getsize(\"/usr/lib/libm.a\")\n", "print(s)" ] }, { "cell_type": "markdown", "id": "c6a3f917-d6b0-4517-93fe-d0d22889d7a6", "metadata": {}, "source": [ "#### Перемещение/копирование/удаление" ] }, { "cell_type": "code", "execution_count": 18, "id": "30545aeb-c4aa-4e3e-9b87-bf8bfafd8129", "metadata": {}, "outputs": [], "source": [ "# Создание одной папки\n", "os.mkdir(\"/tmp/test\")\n", "\n", "# Создание всех папок в пути\n", "os.makedirs(\"/tmp/test/test/test\")\n", "\n", "# Копирование папки рекурсивно\n", "shutil.copytree(\"/tmp/test\", \"/tmp/test2\")\n", "\n", "# Удаление папки (пустой)\n", "os.rmdir(\"/tmp/test/test/test\")\n", "\n", "# Удаление папки с файлами рекурсивно (осторожно!)\n", "shutil.rmtree(\"/tmp/test\")\n", "shutil.rmtree(\"/tmp/test2\")\n", "\n", "with open('/tmp/test.txt', 'w') as f:\n", " f.write('test')\n", "\n", "# Перемещения файла или папки\n", "shutil.move(\"/tmp/test.txt\", \"/tmp/test2.txt\")\n", "\n", "# Копирование файла\n", "shutil.copy(\"/tmp/test2.txt\", \"/tmp/test3.txt\")\n", "\n", "# Удаление файла\n", "os.remove(\"/tmp/test2.txt\")\n", "os.remove(\"/tmp/test3.txt\")\n" ] }, { "cell_type": "markdown", "id": "a4c96ecd-929c-4084-8588-3b46402b4a9e", "metadata": {}, "source": [ "### Список файлов по маске\n", "\n", "Обход по маске — метод glob модуля glob. " ] }, { "cell_type": "code", "execution_count": 20, "id": "02614a37-bbe5-4c08-9308-e6df11a80021", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "/usr/lib/libllvm_gtest_main.a ... /usr/lib/libQt6QmlTypeRegistrar.a\n", "/usr/lib/tdbc1.1.7/libtdbcstub1.1.7.a ... /usr/lib/gprofng/libgp-collectorAPI.a\n" ] } ], "source": [ "import glob\n", "import os\n", "\n", "s1 = glob.glob(\"/usr/lib/*.a\")\n", "print(s1[0], '...', s1[-1])\n", "\n", "s2 = glob.glob(\"/usr/lib/**/*.a\")\n", "print(s2[0], '...', s2[-1])" ] }, { "cell_type": "markdown", "id": "9a92f73b-0fc1-4c51-bce5-dfa702c9e40d", "metadata": {}, "source": [ "| Символ маски | Значение |\n", "| ------------ | -------- |\n", "| `*` | Любое число любых символов |\n", "| `?` | Один любой символ | \n", "| `**` | Обойти все папки рекурсивно |" ] }, { "cell_type": "markdown", "id": "d02c0452-6aba-4577-be98-10c6487942fe", "metadata": {}, "source": [ "### Обход файлов\n", "\n", "Рекурсивный обход файлов метод `walk` модуля `os`." ] }, { "cell_type": "code", "execution_count": 25, "id": "ac6a8210-3086-4b46-8453-6e16f589a1f0", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "/usr/lib\n", "tc ... gssproxy\n", "libQt5Charts.so.5.15 ... libjasper.so.7.0.0\n" ] } ], "source": [ "import os\n", "\n", "for dirname, subdirs, files in os.walk(\"/usr/lib\"):\n", " # dirname — имя текущей папки\n", " print(dirname)\n", " # subdirs — подпапки текущей папки\n", " print(subdirs[0], '...', subdirs[-1])\n", " # files — файлы в текущей папке\n", " print(files[0], '...', files[-1])\n", " break # останавливаем обход принудительно" ] }, { "cell_type": "markdown", "id": "d1c3f699-5272-41ce-9fdc-8bca06f3fb32", "metadata": {}, "source": [ "### Что еще нужно знать о путях\n", "\n", "* Если путь не абсолютный, то он вычисляется от текущей папки программы.\n", "* Обход глубоко вложенных деревьев может занимать очень много времени.\n", "* Разные файловые системы (ФС) имеют различные особенности в частности атрибуты и альтернативные потоки.\n", "* В Windows и POSIX различная схема управления правами на доступ к файлам, а еще есть такие вещи, как SELinux и ACL.\n", "* Минимальный размер файла и папки ~ 4 КБ (зависит от ФС).\n", "* Можно получать уведомления об изменении файлов от ОС, но это платформозависимо (модули `inotify` и `watchdog`).\n", "\n" ] }, { "cell_type": "markdown", "id": "6ab4670c-ad45-435e-b763-d1a083c5c5b4", "metadata": {}, "source": [ "## Работа с файлами\n", "\n", "В зависимости от того, с какими опциями открыт файл в Python он может быть или текстовым (из него будут читаться строки `str`) или двоичным (из него будут читаться байтовые строки `bytes`).\n", "\n", "Структура текстового файла:\n", "\n", "```\n", "Hello world!\n", "Привет мир!\n", "```\n", "\n", "Машинное представление:\n", "\n", "```\n", "00000000 48 65 6c 6c 6f 20 77 6f 72 6c 64 21 0a d0 9f d1 |Hello world!....|\n", "00000010 80 d0 b8 d0 b2 d0 b5 d1 82 20 d0 bc d0 b8 d1 80 |......... ......|\n", "00000020 21 0a |!.|\n", "```\n", "\n", "### Кодировки\n", "\n", "Кодировки бывают:\n", "\n", "* Однобайтовые (кодовые страницы, например, `CP1251`): один байт соответствует одному символу. Диапазон 0-127 одинаков для всех кодовых страниц и включает символы латинского алфавита, знакам препинания, цифры, основные знаки математических действий некоторые непечатные символы. Диапазон 127-255 свой для каждой кодировки.\n", "* Многобайтовые (Unicode)\n", " * С фиксированной шириной (например, `UTF-32`): 2, 4 или 8 байт на каждый символ.\n", " * С плавающей шириной (например, `UTF-8`, `UTF-16`): количество байтов на символ, зависит от символа.\n", "\n", "Популярные кодировки:\n", "\n", "* `ascii` — текстовые файлы IBM, только латиница.\n", "* `cp1251` — текстовые файлы в русской локализации Windows.\n", "* `cp866` — текстовые файлы в русской локализации DOS.\n", "* `utf8` — стандарт для современных Unix-like систем. Кодировка текстовых файлов в Python по умолчанию. Кодировка исходного кода на Python по умолчанию.\n", "\n", "Еще Windows, Unix и Mac отличаются символами конца строки в текстовых файлах:\n", "\n", "* Windows: `\\n\\r`\n", "* Unix: `\\n`\n", "* Mac: `\\r`\n", "\n", "### Работа с текстовыми файлами\n", "\n", "#### Открытие и закрытие файла" ] }, { "cell_type": "code", "execution_count": 29, "id": "0111f149-18e2-4ff2-aa95-a9dc0bb47c47", "metadata": {}, "outputs": [], "source": [ "# Создание файла\n", "with open('/tmp/test.txt', 'w') as f:\n", " f.write('test hello world')\n", " \n", "# Открытие на чтение (текст)\n", "fobj = open(\"/tmp/test.txt\")\n", "\n", "# Открытие на чтение в заданной кодировке (текст)\n", "fobj = open(\"/tmp/test.txt\", encoding=\"utf8\")\n", "fobj = open(\"/tmp/test.txt\", encoding=\"utf8\", newline=\"\\n\")\n", "\n", "# Работа с файлом\n", "\n", "# Закрытие\n", "fobj.close()" ] }, { "cell_type": "markdown", "id": "fe7fb240-186a-4877-bfbb-4f83bd695816", "metadata": {}, "source": [ "Преимущества: простота.\n", "\n", "Проблема: если при работе с файлом возникнет исключение, то файл может останется незакрытым до завершения работы программы.\n", "\n", "#### Работа с файлом через with" ] }, { "cell_type": "code", "execution_count": 30, "id": "d8a0aa4c-6507-42bd-a623-6d8e863c9cda", "metadata": {}, "outputs": [], "source": [ "with open(\"/tmp/test.txt\") as fobj:\n", " pass\n", " # Работа с файлом " ] }, { "cell_type": "markdown", "id": "436d2fb6-12ab-4a9c-bd82-b7f86c9221b7", "metadata": {}, "source": [ " Преимущества: если при работе с файлом возникнет исключение, то он все равно будет закрыт.\n", "\n", "Проблема: рост уровня вложенности.\n", "\n", "#### Чтение файла" ] }, { "cell_type": "code", "execution_count": 32, "id": "3632c0ba-c1c3-40e6-95ad-da1649eebf32", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "test \n", "hello\n", " world\n" ] } ], "source": [ "fobj = open(\"/tmp/test.txt\")\n", "\n", "# Прочесть 5 байт\n", "s = fobj.read(5)\n", "print(s)\n", "\n", "# Прочесть еще 5 байт\n", "s = fobj.read(5)\n", "print(s)\n", "\n", "# Прочесть файл до конца\n", "s = fobj.read()\n", "print(s)\n", "\n", "fobj.close()" ] }, { "cell_type": "markdown", "id": "706747b5-2283-44d8-8b4d-7030c3b14311", "metadata": {}, "source": [ "#### Чтение в список строк" ] }, { "cell_type": "code", "execution_count": 33, "id": "adf099ff-fd30-495f-9744-8644d246af32", "metadata": {}, "outputs": [], "source": [ "fobj = open(\"/tmp/test.txt\")\n", "\n", "# Прочесть файл \n", "sa = fobj.readlines()\n", "# sa == ['Hello world!\\n','Привет мир!\\n']\n", "\n", "fobj.close()" ] }, { "cell_type": "markdown", "id": "640096e2-a8a1-427e-b138-2ad80c5193c3", "metadata": {}, "source": [ "Преимущества: Можно обращаться к отдельным строкам.\n", "\n", "Недостатки: Файл приходится целиком загружать в память.\n", "\n", "#### Чтение файла построчно" ] }, { "cell_type": "code", "execution_count": 36, "id": "43135917-8a9e-44e7-a8b4-cebacfce22e4", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "test hello world\n" ] } ], "source": [ "fobj = open(\"/tmp/test.txt\")\n", "\n", "for line in fobj:\n", " print(line)\n", "\n", "fobj.close()" ] }, { "cell_type": "markdown", "id": "9bb7be43-4fac-432e-a6ec-141e378c8aa0", "metadata": {}, "source": [ " Преимущества: В каждый момент времени в память загружена только одна строка.\n", "\n", "Недостатки: Некоторые алгоритмы обработки файлов трудно реализовать.\n", "\n", "#### Перемещение по файлу\n", "\n", "По любому файлу (текстовому или двоичному) можно перемещаться по номеру байта. (осторожнее с Unicode, если номер окажется посередине многобайтового символа — будет ошибка). " ] }, { "cell_type": "code", "execution_count": 38, "id": "34533d56-b9b4-4b84-94ca-1731c3203d87", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "test hello world\n", "16\n", "rld\n", "test hello world\n" ] } ], "source": [ "fobj = open(\"/tmp/test.txt\")\n", "\n", "s = fobj.read()\n", "print(s)\n", "\n", "#Получить текущее положение в файле (в байтах)\n", "ps = fobj.tell()\n", "print(ps)\n", "\n", "#Перейти на заданное положение в файле (в байтах)\n", "fobj.seek(13)\n", "\n", "s = fobj.read(5)\n", "print(s)\n", "\n", "# Перейти в начало\n", "fobj.seek(0)\n", "\n", "s = fobj.read()\n", "print(s)\n", "\n", "fobj.close()" ] }, { "cell_type": "markdown", "id": "293e63fc-f838-4901-b135-7b254acde259", "metadata": {}, "source": [ "### Открытие файла на запись\n", "\n", "По умолчанию файлы открываются только на чтение.\n", "\n", "Для записи в файл его нужно открыть с соответствующей опцией (второй аргумент `open`), которая по традиции обозначается буквой. Если файл не существует, он будет создан, если существует, то возможны варианты.\n", "\n", "| Опция | Режим | Текущая позиция чтения/записи |\n", "| ----- | ----- | ----- |\n", "| пусто или `r` | Только чтение | начало |\n", "| `w` | Запись, файл обрезается до пустого | начало |\n", "| `r+` | Чтение и запись, файл не обрезается | начало |\n", "| `a` | Запись, файл не обрезается | конец |\n", "| `a+` | Чтение и запись, файл не обрезается | конец |\n", "\n", "### Запись в файл" ] }, { "cell_type": "code", "execution_count": 39, "id": "421c32ba-26fa-4801-a27e-a12458151b3e", "metadata": {}, "outputs": [], "source": [ "fobj = open(\"/tmp/test.txt\", \"w\")\n", "\n", "wbc = fobj.write(\"Hello world!\\n\")\n", "# wbc == 13\n", "\n", "fobj.close()" ] }, { "cell_type": "code", "execution_count": 40, "id": "6c55434c-49dc-44b4-8821-db7fd98e11b6", "metadata": {}, "outputs": [], "source": [ "fobj = open(\"/tmp/test.txt\", \"r+\")\n", "\n", "content = fobj.read()\n", "\n", "wbc = fobj.write(\"Hello world!\\n\")\n", "# wbc == 13\n", "wt = fobj.tell()\n", "# wt == 26\n", "\n", "fobj.close()" ] }, { "cell_type": "markdown", "id": "5cbe3b57-5c12-44a3-8d81-b4b078a3bfbb", "metadata": {}, "source": [ "### Двоичные файлы\n", "\n", "Работа с двоичным файлом аналогична работе с текстовым, однако меняются опции открытия файла. И читать/писать придется объекты типа `bytes`, а не `str`.\n", "\n", "| Опция | Режим | Текущая позиция чтения/записи |\n", "| ----- | ----- | ----------------------------- |\n", "| `b` или `rb` | Только чтение | начало |\n", "| `wb` | Запись, файл обрезается до пустого | начало |\n", "| `rb+` | Чтение и запись, файл не обрезается | начало |\n", "| `ab` | Запись, файл не обрезается | конец |\n", "| `ab+` | Чтение и запись, файл не обрезается | конец |\n", "\n", "### Чтение и запись" ] }, { "cell_type": "code", "execution_count": 44, "id": "1414643f-0dfc-485c-8dc6-65d5dd93f773", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "b'\\xd0\\x9f\\xd1\\x80\\xd0\\xb8\\xd0\\xb2\\xd0\\xb5\\xd1\\x82 \\xd0\\xbc\\xd0\\xb8\\xd1\\x80!\\n'\n", "21\n", "b'\\xd0\\x9f\\xd1\\x80\\xd0\\xb8\\xd0\\xb2\\xd0\\xb5\\xd1\\x82 \\xd0\\xbc\\xd0\\xb8\\xd1\\x80!\\n'\n" ] } ], "source": [ "fobj = open(\"/tmp/test.txt\", \"ab+\")\n", "\n", "ds = \"Привет мир!\\n\"\n", "dbytes = ds.encode('utf8')\n", "print(dbytes)\n", "\n", "wbc = fobj.write(dbytes)\n", "print(wbc)\n", "\n", "fobj.seek(0)\n", "\n", "dbs = fobj.read(24)\n", "print(dbs)\n", "\n", "fobj.close()\n", "\n", "os.remove(\"/tmp/test.txt\") # Очистка" ] }, { "cell_type": "markdown", "id": "3531983c-fc8f-4881-b1d0-e098ff229c9c", "metadata": {}, "source": [ "### Чтение и запись Python объектов\n", "\n", "Самый простой способ сохранить структурированные данные в Python — воспользоваться модулем `pickle` стандартной библиотеки. " ] }, { "cell_type": "code", "execution_count": 47, "id": "361eef15-366a-4fde-9104-d39f6de7c383", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'keyA': [1, 2, 3], 'keyB': ('строка', b'bytes'), 'keyC': 'Тест'}\n" ] } ], "source": [ "import pickle\n", "\n", "sdata = {'keyA': [1, 2, 3],\n", " 'keyB': (\"строка\", b\"bytes\"),\n", " 'keyC': 'Тест' }\n", "\n", "with open('/tmp/data.pickle', 'wb') as fobj:\n", " pickle.dump(sdata, fobj)\n", "\n", "\n", "with open('/tmp/data.pickle', 'rb') as fobj:\n", " tdata = pickle.load(fobj)\n", "\n", "print(tdata)\n", "\n", "os.remove(\"/tmp/data.pickle\") # Очистка" ] }, { "cell_type": "markdown", "id": "ea128d1e-bb22-4951-ac52-04e59fd11dea", "metadata": {}, "source": [ "Достоинства: Простота. Сохраняет практически любой Python объект.\n", "\n", "Недостатки: Только для Python. Небезопасно с точки зрения обмена данными.\n", "\n", "### Чтение и запись числовых массивов\n", "\n", "Для работы с массивами чисел в заданном машинном формате в Python есть класс `array`. " ] }, { "cell_type": "code", "execution_count": 51, "id": "93554e03-2a00-440b-b355-2a273372ec59", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "array('f', [2.0, 4.5, 3.299999952316284])\n" ] } ], "source": [ "import array\n", "\n", "ar = array.array('f', [2.0, 4.5, 3.3])\n", "\n", "with open('/tmp/array.dat', 'wb') as fobj:\n", " ar.tofile(fobj)\n", "\n", "ar2 = array.array('f')\n", "\n", "with open('/tmp/array.dat', 'rb') as fobj:\n", " ar2.fromfile(fobj, 3)\n", "\n", "print(ar2)\n", "\n", "os.remove(\"/tmp/array.dat\") # Очистка" ] }, { "cell_type": "markdown", "id": "0ef415be-0c9c-497c-af8c-858c1e8de119", "metadata": {}, "source": [ "#### Некоторые форматы\n", "\n", "| Формат | C-тип | Python-тип | Размер элемента в байтах |\n", "| ---- | ---- | ---- | ---- |\n", "| `b` | `signed char` | `int` | `1` |\n", "| `B` | `unsigned char` | `int` | `1` |\n", "| `l` | `signed long` | `int` | `4` |\n", "| `L` | `unsigned long` | `int` | `4` |\n", "| `f` | `float` | `float` | `4` |\n", "| `d` | `double` | `float` | `8` |\n", "\n", "Подробная таблица форматов.\n", "\n", "Достоинства: Простота. Переносимость.\n", "\n", "Недостатки: Можно сохранять только массивы чисел.\n", "\n", "#### Чтение и запись С-структур\n", "\n", "Для работы со структурами данных C в Python есть модуль struct." ] }, { "cell_type": "code", "execution_count": 64, "id": "4eedd7d9-e08d-4244-b6fd-aeadc106f8bd", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(b'Hello\\x00\\x00\\x00\\x00\\x00', 2, 3, 19, 19)\n" ] } ], "source": [ "import struct\n", "\n", "st = struct.Struct('10sIIxBB')\n", "data = st.pack(b\"Hello\",2,3,19,19)\n", "# data == b'Hello\\x00\\x00\\x00\\x00\\x00...\n", "\n", "with open('/tmp/struct.dat', 'wb') as fobj:\n", " fobj.write(data)\n", "\n", "with open('/tmp/struct.dat', 'rb') as fobj:\n", " td = fobj.read() \n", "\n", "values = st.unpack(td)\n", "print(values)" ] }, { "cell_type": "markdown", "id": "62b57e21-305f-4e65-aaba-5abe8644ea19", "metadata": {}, "source": [ "Если структура данных сложная, но есть ее описание на С, то можно воспользоваться модулем `cffi`.\n", "\n", "#### Некоторые форматы struct\n", "\n", "Управление порядком байт `<` — little-endian, `>` — big-endian.\n", "\n", "| Формат | C-тип | Python-тип | Размер элемента в байтах |\n", "| ---- | ---- | ---- | ---- |\n", "| `x` |\t— | — | `1` |\n", "| `b` |\t`signed char` |\t`int` |\t`1` |\n", "| `B` |\t`unsigned char` | `int` | `1` |\n", "| `i` |\t`int` |\t`int` |\t`4` |\n", "| `I` |\t`unsigned int` | `int` | `4` |\n", "| `f` |\t`float` | `float` | `4` |\n", "| `d` |\t`double` | `float` | `8` |\n", "| `s` |\t`char[]` | `bytes` | `1` |\n", "\n", "Подробная таблица форматов.\n", "\n", "Достоинства: Переносимость.\n", "\n", "Недостатки: Нужно вручную описывать формат структур, думать о порядке байт и выравнивании.\n", "\n", "#### Файлы отображенные в память\n", "\n", "Есть возможность работать с файлом, как с массивом байтов не загружая его в память полностью. Для этого используется отображения файла в память. " ] }, { "cell_type": "code", "execution_count": 65, "id": "d1a80a69-89ff-4afa-8547-4ba0a93e64e5", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "b'Hello'\n", "b'He***\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x00\\x13\\x13'\n" ] } ], "source": [ "import mmap\n", "\n", "fobj = open(\"/tmp/struct.dat\", \"r+b\")\n", "\n", "# Создаем отображенный файл:\n", "# Первый аргумент - дескриптор заранее открытого файла\n", "# Второй - начальное положение в байтах (можно отобразить не весь файл)\n", "mapobj = mmap.mmap(fobj.fileno(), 0)\n", "\n", "# Работаем с файлом как с массивом байтов\n", "print(mapobj[:5])\n", "mapobj[2:5] = b\"***\"\n", "\n", "mapobj.close()\n", "fobj.close()\n", "\n", "with open(\"/tmp/struct.dat\", \"rb\") as fobj:\n", " print(fobj.read())\n", "\n", "os.remove(\"/tmp/struct.dat\") # Очистка" ] }, { "cell_type": "markdown", "id": "18ef2404-bfd1-4b3d-9f32-8a5941c45961", "metadata": {}, "source": [ "Также файл отображенный на память поддерживает стандартные методы для работы с файлами, напрмер `read` и `write`.\n", "\n", "#### Псевдофайлы\n", "\n", "С точки зрения Python файлы это просто объекты имеющие определенные методы.\n", "\n", "В Python есть классы объекты которых выглядят, как утки файлы, но файлами не являются. Например `StringIO` и `BytesIO` из модуля `io` стандартной библиотеки. " ] }, { "cell_type": "code", "execution_count": 67, "id": "f0a40b70-bbca-4f1c-83a3-a54b1bebfb9f", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " line.\n", "\n", "First line.\n", "\n" ] } ], "source": [ "import io\n", "\n", "fobj = io.StringIO()\n", "fobj.write('First line.\\n')\n", "fobj.seek(5)\n", "\n", "s = fobj.read()\n", "print(s)\n", "ss = fobj.getvalue()\n", "print(ss)\n", "\n", "fobj.close()" ] }, { "cell_type": "markdown", "id": "e2ba16e6-f316-4bf4-9460-5df9eeaa5abe", "metadata": {}, "source": [ "### Сводная таблица методов файлов\n", "\n", "| Метод | Действие | Возвращает |\n", "| ----- | ----- | ----- |\n", "| `read()` | Читает файл от текущей позиции до конца | `str`/`bytes` |\n", "| `read(n)` | Читает n символов или байт | `str`/`bytes` |\n", "| `readlines()` | Читает текстовый файл от текущей позиции до конца, разбивает результат по `os.linesep` | `str`/`bytes` |\n", "| `write(x)` | Записывает в файл с текущей позиции `str`/`bytes`, возвращает число записанных символов / байт | `int` |\n", "| `tell()` | Текущее положение в файле всегда в байтах | `int` |\n", "| `seek(n)` | Перемещается на позицию n в файле всегда в байтах, возвращает позицию на которую удалось переместиться | `int` |\n", "| `flush()` | Записывает сбрасывает буфер на диск | `None` |\n", "| `close()` | Закрывает файл | `None` |\n", "\n", "### Особые файлы\n", "\n", "При запуске скрипта автоматически открываются 3 файла доступных в модуле `sys`:\n", "\n", "| Файл | Тип | Смысл |\n", "| ---- | ---- | ---- |\n", "| `sys.stdout` | `w` | вывод в консоль | \n", "| `sys.stdout.buffer` | `wb` | вывод в консоль |\n", "| `sys.stderr` | `w` | сообщения об ошибках |\n", "| `sys.stderr.buffer` | `wb` | сообщения об ошибках |\n", "| `sys.stdin` | `r` | ввод с консоли |\n", "| `sys.stdin.buffer` | `rb` | ввод с консоли |\n", "\n", "Такие операции как `print`, `input` сводятся к работе с этими файлами.\n", "\n", "### Что еще нужно знать о файлах\n", "\n", "* Как правило `read`/`write` — слишком низкий уровень.\n", "* По умолчанию запись в файл производится не в момент `write`, а в момент, когда накопится достаточно данных для записи. Для того, чтобы выполнить запись немедленно есть метод `flush`.\n", "* Если файл больше десятка мегабайт, загружать его в память целиком — не очень хорошая идея.\n", "* При работе с двоичными файлами, помните, что есть `byteorder`.\n", "* Будьте осторожны с записью и перезаписью файлов.\n", "\n" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "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.12.6" } }, "nbformat": 4, "nbformat_minor": 5 }