diff --git a/08 - Files/08 - Files.ipynb b/08 - Files/08 - Files.ipynb index e4e2f50..e47cbeb 100644 --- a/08 - Files/08 - Files.ipynb +++ b/08 - Files/08 - Files.ipynb @@ -1,2371 +1,2798 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "id": "YIgCCwN5a39O" - }, - "source": [ - "# Работа с файлове в Python\n", - "План на лекцията:\n", - "- Кратка интродукция за работата с пътища, Windows и Unix\n", - "- Представяне на файловете в Python\n", - "- Четене на файлове\n", - "- Писане на файлове\n", - "- Работа с файлове и пътища\n", - "- Използване на `with`\n", - "- `tell` и `seek`\n", - "- Примери\n", - "- Задачи" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "LnxJAYYb63dN" - }, - "source": [ - "### Предварителна подготовка " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "L02fnebn7I_E", - "outputId": "d119aaac-5070-42ec-f312-3d3f5515c571" - }, - "outputs": [], - "source": [ - "!mkdir files\n", - "!curl -o files/lorem_ipsum.txt https://raw.githubusercontent.com/lyubolp/PythonCourse2022/08_files/08%20-%20Files/files/1.txt\n", - "!echo \"hello\" > files/hello.txt" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "3Fd8dyDSliLn" - }, - "source": [ - "## Кратка интродукция за работата с пътища, Windows и Unix." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "1FQAdGyXlkjs" - }, - "source": [ - "Път в една файлова система посочва локацията и името на даден обект (бил той файл или директория). Пример за път в Linux/MacOS е `/home/user/myfile.txt`, а в Windows - `C:\\Users\\user\\myfile.txt`. \n", - "\n", - "Забелязва се, че пътищата в Windows и Unix-базираните ОС се разделят с различни черти. Нашият Python код трябва да е съвместим и с двата начина за разделя на пътища. \n", - "\n", - "За наше улеснение, Python предлага функцията `os.path.join`, която по подадени имена на директории/файлове, конструира правилния спрямо нашия OS път." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 36 - }, - "id": "FR2Fp_MrnHDt", - "outputId": "aa05f14d-0ea4-4792-9495-37093431e58d" - }, - "outputs": [], - "source": [ - "import os\n", - "\n", - "os.path.join('/home', 'lyubo', 'myfile.txt')" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "RGGzX2_znL_z" - }, - "source": [ - "Понеже кодът е изпълнен под Linux, получаваме Unix-ски път. Ако изпълним обаче същия код под Windows, ще получим правилен Windows-ки път. " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "KPWujZmdo10D" - }, - "source": [ - "## Представяне на файловете в Python" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "FBsGm74Yo6LW" - }, - "source": [ - "Python предоставя API за работа с файлове и потоци. Python работи с т.нар. \"file objects\" - това може да са файлове на диска, sockets, pipes и други потоци.\n", - "\n", - "Освен с текстови файлове, можем да работим и с бинарни файлове. За момента обаче, ще се спрем само върху текстовите файлове." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "e0ME86yzrRuJ" - }, - "source": [ - "Можем да отворим един файл за работа, с помощта на функцията `open`. В най-простия си вид, тя приема път към файл. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "9uztE6pzrYj5", - "outputId": "beeb5370-efd0-4a0b-e148-9b3de6c569c8" - }, - "outputs": [], - "source": [ - "fd = open(os.path.join('files', 'lorem_ipsum.txt'))\n", - "\n", - "print(fd)\n", - "\n", - "fd.close()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "4ohPpwYurg55" - }, - "source": [ - "След като приключим работа с един файл, не трябва да забравяме да го затворим. Затварянето на един файл става с помощта на `close` метода. " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "lpRD3mZarn3x" - }, - "source": [ - "Забелязваме, че обекта който получаваме като резултат от `open` има име (`files/lorem_ipsum.txt`), режим (`r`) и кодиране (`UTF-8`).\n", - "\n", - "Един файл може да бъде отворен в няколко различни режима:\n", - "- `r` - отваря файла за четене\n", - "- `w` - отваря файла за писане, като файла първо бива зачистен\n", - "- `a` - отваря файла за писане, като новото съдържание се записва в края на файла\n", - "- `x` - създава файла, ако не съществува. Ако файла вече съществува, се хвърля `FileExistsError`\n", - "- `b` - отваря файл в бинарен режим\n", - "- `t` - отваря файл в текстови режим\n", - "- `+` - отваря файла за четене и писане\n", - "\n", - "Освен режима на отваряне, можем да променим и кодирането, с което се опитваме да четем файла. По подразбиране, използваме `UTF-8`. " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "RlrURy5tmAHa" - }, - "source": [ - "Ако се опитаме да запишем файл в несъществуваща директория, ще получим грешка" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 239 - }, - "id": "jcixN1mWl_sN", - "outputId": "f028fd68-4a0b-49fb-f998-1bdc8307e1d7" - }, - "outputs": [], - "source": [ - "fd = open(os.path.join('files', 'non_existing_dir', 'new_file.txt'), 'w')\n", - "\n", - "fd.write('content')\n", - "\n", - "fd.close()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "UU_sozqXbJhx" - }, - "source": [ - "## Четене на файлове" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "D27sqq5ilMSp" - }, - "source": [ - "В Python имаме три метода, чрез които можем да четем от файлове: `read`, `readline` и `readlines`. " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "jJNafCFk12bQ" - }, - "source": [ - "Нека първо разгледаме метода `read`. Той прочита целия файл и запазва съдържанието му в променлива, като един голям низ. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "h92FBNCNat7U", - "outputId": "d4ad48b3-f35a-4960-89ec-d3d294d138c3" - }, - "outputs": [], - "source": [ - "import os\n", - "\n", - "fd = open(os.path.join('files', 'lorem_ipsum.txt'))\n", - "\n", - "content = fd.read()\n", - "print(f'content={content}')\n", - "\n", - "fd.close()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "vGKGqRa-1K_m" - }, - "source": [ - "Веднъж прочетен един файл, следващото прочитане ще ни върне празен низ. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "-Xfg_I0NJJ9C", - "outputId": "ef0f129a-d0b4-43c6-b9b3-e22a53d2b6f8" - }, - "outputs": [], - "source": [ - "fd = open(os.path.join('files', 'lorem_ipsum.txt'))\n", - "\n", - "content = fd.read()\n", - "print(f'content={content}')\n", - "\n", - "content = fd.read()\n", - "print(f'content={content}')\n", - "\n", - "fd.close()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "ULGoY5x72lCb" - }, - "source": [ - "Друг вариант за четене на файл, е ред по ред - това става с помощта на метода `readline`. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "oK9Xi-Gu2zr7", - "outputId": "d297f349-c3db-4142-b098-f0cc2426f054" - }, - "outputs": [], - "source": [ - "import os\n", - "fd = open(os.path.join('files', 'lorem_ipsum.txt'))\n", - "\n", - "content = fd.readline()\n", - "print(f'content={content}')\n", - "\n", - "content = fd.readline()\n", - "print(f'content={content}')\n", - "\n", - "fd.close()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "_DXlyrPY315L" - }, - "source": [ - "След прочитане на последния ред, `readline` връща празен низ." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "yVRgVthN3vix", - "outputId": "36bfcf22-3204-4e19-d5e3-015309f9efa4" - }, - "outputs": [], - "source": [ - "import os\n", - "fd = open(os.path.join('files', 'lorem_ipsum.txt'))\n", - "\n", - "content = fd.readline()\n", - "print(f'content={content}')\n", - "\n", - "while content != '':\n", - " content = fd.readline()\n", - " print(f'content={content}')\n", - "\n", - "fd.close()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "6nLUpVFq4Ld4" - }, - "source": [ - "Ако искаме да получим списък от всички редове във файл, можем да използваме `readlines`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "xcmi0pcO4ULY", - "outputId": "0fab048c-1f0e-4bf7-bea1-3262dbb79800" - }, - "outputs": [], - "source": [ - "import os\n", - "fd = open(os.path.join('files', 'lorem_ipsum.txt'))\n", - "\n", - "content = fd.readlines()\n", - "print(f'content={content}')\n", - "\n", - "fd.close()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "zPRIvtWb4Ycu", - "outputId": "cba32c68-7e4f-474c-88d9-76a72d75d0ed" - }, - "outputs": [], - "source": [ - "import os\n", - "fd = open(os.path.join('files', 'lorem_ipsum.txt'))\n", - "\n", - "content = fd.readlines()\n", - "\n", - "for line in content:\n", - " print(f'content={line}')\n", - "\n", - "fd.close()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "URkhviux8oQn" - }, - "source": [ - "Вместо да използваме `readlines`, можем да итерираме директно по файл обекта." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "6uiK1ioa9zCq", - "outputId": "e6ee4b1d-9bcb-4f27-ce4c-30bbc78319b5" - }, - "outputs": [], - "source": [ - "import os\n", - "fd = open(os.path.join('files', 'lorem_ipsum.txt'))\n", - "\n", - "for line in fd:\n", - " print(f'content={line}')\n", - "\n", - "fd.close()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "90Il8AMhbLu2" - }, - "source": [ - "## Писане на файлове" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "GUyazAnfSESG" - }, - "source": [ - "За писане на файлове в Python може да използваме методите `write` и `writelines`" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "URaUQ1hVS_Dx" - }, - "source": [ - "Методът `write` записва низ във файла. Позицията зависи от начина по който е отворен файла (с или без изтриване на текущото съдържание)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "epMK7--sUZuH" - }, - "outputs": [], - "source": [ - "import os\n", - "fd = open(os.path.join('files', 'hello_world.txt'), 'w')\n", - "\n", - "fd.write('Hello world')\n", - "\n", - "fd.close()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "J0n45OqOh8D2", - "outputId": "db5e12b5-ca9f-4172-826b-bb7ca61f5c94" - }, - "outputs": [], - "source": [ - "import os\n", - "fd = open(os.path.join('files', 'hello_world.txt'))\n", - "\n", - "content = fd.read()\n", - "print(content)\n", - "fd.close()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "SiZ4QZjZiZOo" - }, - "source": [ - "Припомням, че ако отворим файла в режим `w`, то ще изтрием текущото му съдържание." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "VJPqmvsgiYrZ" - }, - "outputs": [], - "source": [ - "import os\n", - "\n", - "fd = open(os.path.join('files', 'my_important_file.txt'), 'w')\n", - "fd.write('Really important')\n", - "fd.close()\n", - "\n", - "# Oops, forgot to write something down\n", - "\n", - "fd = open(os.path.join('files', 'my_important_file.txt'), 'w')\n", - "fd.write('Should really not forget this')\n", - "fd.close()\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "nVBAP46YiwhL", - "outputId": "9c7a298f-d682-4886-8713-fbece2640792" - }, - "outputs": [], - "source": [ - "import os\n", - "fd = open(os.path.join('files', 'my_important_file.txt'))\n", - "\n", - "content = fd.read()\n", - "print(content)\n", - "fd.close()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "wuq58B4vi5Ub" - }, - "source": [ - "Ако искаме да пишем в края на файла, трябва да използваме режим `a`" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "bwjQXxlci9Kx" - }, - "outputs": [], - "source": [ - "import os\n", - "\n", - "fd = open(os.path.join('files', 'my_important_file_2.txt'), 'a')\n", - "fd.write('Really important\\n')\n", - "fd.close()\n", - "\n", - "# Oops, forgot to write something down\n", - "\n", - "fd = open(os.path.join('files', 'my_important_file_2.txt'), 'a')\n", - "fd.write('Should really not forget this\\n')\n", - "fd.close()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "sdlaFvC6jJ1x", - "outputId": "fde00371-b2c4-4ff7-97c0-cc2ffce4b43b" - }, - "outputs": [], - "source": [ - "import os\n", - "fd = open(os.path.join('files', 'my_important_file_2.txt'))\n", - "\n", - "content = fd.read()\n", - "print(content)\n", - "fd.close()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "MMU2OzCFjqzI" - }, - "source": [ - "Методът `writelines` приема списък от \"редове\", които да бъдат записани във файла.\n", - "\n", - "**Забележка**: `writelines` не добавя автоматично нови редове след всеки елемент, затова се очаква всеки елемент от списъка да съдържа нов ред в себе си. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "Cl81-OtJkvXW" - }, - "outputs": [], - "source": [ - "import os\n", - "\n", - "fd = open(os.path.join('files', 'writelines_example.txt'), 'w')\n", - "\n", - "lines_to_write = ['hello\\n', 'this\\n', 'are\\n', 'my\\n', 'lines\\n']\n", - "fd.writelines(lines_to_write)\n", - "\n", - "fd.close()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "p1P40tosk-5A", - "outputId": "e3c801f9-f824-46c2-d7c1-18ea44faf72e" - }, - "outputs": [], - "source": [ - "import os\n", - "fd = open(os.path.join('files', 'writelines_example.txt'))\n", - "\n", - "content = fd.read()\n", - "print(content)\n", - "fd.close()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "mk0qWw2GWTFP" - }, - "source": [ - "Тук е добре да се отбележи, че можем да записваме и други типове данни, освен низове (е не точно, но...).\n", - "\n", - "Стига даден тип (или обект) да има низово представяне, можем да го запишем във файл." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "E67N2bZvWjxq", - "outputId": "f7a352ca-8287-4bb6-8072-73860b20d11a" - }, - "outputs": [], - "source": [ - "import os\n", - "\n", - "other_types_in_files = os.path.join('files', 'other_types_in_files.txt')\n", - "\n", - "fd = open(other_types_in_files, 'w')\n", - "\n", - "fd.write(str(2) + '\\n')\n", - "fd.write(str([2, 3, 4]) + '\\n')\n", - "fd.write(str({'a': 2, 'b': 3, 'c': 4}) + '\\n')\n", - "fd.write(str((2, 3)) + '\\n')\n", - "\n", - "fd.close()\n", - "\n", - "fd = open(other_types_in_files)\n", - "content = fd.read()\n", - "print(content)\n", - "fd.close()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "Nlb97sGOf4s8" - }, - "source": [ - "## Работа с файлове и пътища" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "oe4Ti1UBhsVW" - }, - "source": [ - "Python предлага удобен начин за работа с файлове. Вградената библиотека `os` съдържа всичко необходимо за работата с файлове и директории. \n", - "\n", - "Ще разгледаме как можем да:\n", - "- Прегледаме съдържанието на директория\n", - "- Преместим файл\n", - "- Изтрием файл\n", - "- Създадем директория\n", - "- Преместим директория\n", - "- Изтрием директория\n", - "- Обща работа с пътища\n", - "- Обхождане на директории" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "HbjmmlfMYpVo" - }, - "source": [ - "### Преглеждане на съдържание на директория" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "Ks5R8-anYyA-" - }, - "source": [ - "Можем да видим съдържанието на директория като използваме `os.listdir` метода. Той ни връща списък от низове, съдържащи имената на директориите и файловете в исканата от нас папка. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "KMuyJeL_Zw8t", - "outputId": "81420cb0-3fff-475a-83cc-b362f57e7317" - }, - "outputs": [], - "source": [ - "import os\n", - "\n", - "print(os.listdir('files'))" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "G3Jqt8kLi8W0" - }, - "source": [ - "### Преместване на файл" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "njIXBgExNpyD" - }, - "source": [ - "Преместването на файл става чрез \"преименуването му\" (или всъщност, преименуването на файл е преместването му като файл с друго име 🤔) - това в Python става с помощта на функцията `os.rename`. Тя приема два аргумента - source път и destination път (т.е. старото и новото име на файла)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "1g2-mwQLOnro", - "outputId": "c6c7367e-ace7-4d91-bbff-30feed94262c" - }, - "outputs": [], - "source": [ - "import os\n", - "\n", - "file_to_be_moved = os.path.join('files', 'to_be_moved.txt')\n", - "\n", - "fp = open(file_to_be_moved, 'w')\n", - "fp.write('This file is to be moved')\n", - "fp.close()\n", - "\n", - "print(f'Before = {os.listdir(\"files\")}')\n", - "\n", - "file_moved = os.path.join('files', 'file_moved.txt')\n", - "\n", - "os.rename(file_to_be_moved, file_moved)\n", - "\n", - "print(f'After = {os.listdir(\"files\")}')" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "CKGnXOydcpvy" - }, - "source": [ - "Ако вече съществува файл със същото име се случват едно от две неща:\n", - "- Ако кодът се изпълнява под Windows, се хвърля `FileExistsError`. \n", - "- Ако кодът се изпълнява под Linux/MacOS и имаме права върху файла върху който ще пишем, той ще бъде презаписан" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "qRE0bS-BliKM" - }, - "source": [ - "Нека разгледаме следната ситуация: имаме файл `files/a/file.txt`, който искаме да преместим в `files/b`, но директорията `b` не съществува." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "p3IzgYI0l1Rl" - }, - "outputs": [], - "source": [ - "!mkdir files/a" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 204 - }, - "id": "t7hy8TpPlrsS", - "outputId": "940ab36b-b456-49d1-e9a7-8e37374723ae" - }, - "outputs": [], - "source": [ - "import os\n", - "\n", - "file_to_be_moved = os.path.join('files', 'a', 'file.txt')\n", - "\n", - "fp = open(file_to_be_moved, 'w')\n", - "fp.write('This file is to be moved')\n", - "fp.close()\n", - "\n", - "os.rename(file_to_be_moved, os.path.join('files', 'b', 'file.txt'))" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "baI__6ycM8IS" - }, - "source": [ - "### Изтриване на файл" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "TbpItit-NzqO" - }, - "source": [ - "Изтриването на файл се случва чрез метода `os.remove`. Той приема един аргумент - пътят към файла, който ще бъде изтрит." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "MuNNNLMJpXNS", - "outputId": "f31fc9f8-4db8-499f-c6de-9d321cef7907" - }, - "outputs": [], - "source": [ - "import os\n", - "\n", - "file_path = os.path.join('files', 'to_removed.txt')\n", - "fp = open(file_path, 'w')\n", - "fp.write('This file will be deleted')\n", - "fp.close()\n", - "\n", - "print(f'Before = {os.listdir(\"files\")}')\n", - "\n", - "os.remove(file_path)\n", - "\n", - "print(f'After = {os.listdir(\"files\")}')" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "4lDTPY-6dsce" - }, - "source": [ - "Ако се опитаме да изтрием директория с `os.remove`, ще получим грешка `IsADirectoryError`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 204 - }, - "id": "7n1HOZdUdzQn", - "outputId": "ed4ade21-2c0a-4a40-bb08-ade1059a8c79" - }, - "outputs": [], - "source": [ - "import os\n", - "\n", - "os.remove('files')" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "yzM5ffT8eSVt" - }, - "source": [ - "А ако опитаме да изтрием файл, който не съществува, ще получим грешка `FileNotFoundError`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 204 - }, - "id": "gwHS4rtYeX3f", - "outputId": "af413bc7-5a19-4a8f-bdb6-90de264f714b" - }, - "outputs": [], - "source": [ - "import os\n", - "\n", - "non_existant_path = os.path.join('files', 'this_file_does_not_exist.txt')\n", - "\n", - "os.remove(non_existant_path)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Копиране на файлове" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "В Python можем да копираме файлове с помощта на `shutil` библиотеката. Тя ни предоставя методи за копиране на файлове.\n", - "\n", - "Ще разгледаме част от тях:\n", - "\n", - "- `shutil.copy()` - копира файл от едно място на друго. Приема два аргумента - source и target пътища. Ако файлът вече съществува на новото място, ще бъде презаписан. Ако target е директория, ще се запише копие на файла в тази директория със същото име.\n", - "- `shutil.copy2()` - работи по същия начин като `shutil.copy()`, но запазва и метаданните на файла (например времето на създаване)\n", - "- `shutil.copystat()` - копира само метаданните на файла\n", - "- `shutil.copytree()` - копира директория и всички файлове в нея. Приема два аргумента - source и target пътища. " - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Before = ['1.txt']\n", - "After = ['1_2.txt', '1.txt', '1_1.txt']\n" - ] - } - ], - "source": [ - "import os\n", - "import shutil\n", - "\n", - "print(f'Before = {os.listdir(\"files\")}')\n", - "\n", - "shutil.copy(os.path.join('files', '1.txt'), os.path.join('files', '1_1.txt'))\n", - "shutil.copy2(os.path.join('files', '1.txt'), os.path.join('files', '1_2.txt'))\n", - "\n", - "print(f'After = {os.listdir(\"files\")}')" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "Ekhha8biM-Ps" - }, - "source": [ - "### Създаване на директория" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "SiGgH-3r_zGq" - }, - "source": [ - "В Python създаването на директория става чрез метода `os.mkdir`. Той приема пътя към директорията, която да бъде създадена. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "Y8_j9oJHAUA7", - "outputId": "c9c97bf8-accd-42fa-b28c-9dcf4d42dfd4" - }, - "outputs": [], - "source": [ - "import os\n", - "\n", - "print(f'Before = {os.listdir(\"files\")}')\n", - "\n", - "file_path = os.path.join('files', 'mkdir_example')\n", - "os.mkdir(file_path)\n", - "\n", - "print(f'After = {os.listdir(\"files\")}')" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "kbA7dS_PAzTr" - }, - "source": [ - "Само за информация: Можем да зададем права на директорията, с помощта на аргумента `mode`. " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "E9bfpXokDlB_" - }, - "source": [ - "Ако се опитаме да създадем директория, която вече съществува, ще получим `FileExistsError`. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 204 - }, - "id": "jUSXZTQCD0v-", - "outputId": "4984bab1-7b64-48b1-bcf1-781960502d79" - }, - "outputs": [], - "source": [ - "import os\n", - "\n", - "file_path = os.path.join('files', 'mkdir_existing_directory')\n", - "os.mkdir(file_path)\n", - "\n", - "os.mkdir(file_path)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "lTXna1gtBs0g" - }, - "source": [ - "Нека опитаме да създадем няколко нови директории, една под друга. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 276 - }, - "id": "sp1qFjZGC81F", - "outputId": "1a2cf982-7fa2-4024-b6a8-1ec473a978ac" - }, - "outputs": [], - "source": [ - "import os\n", - "\n", - "print(f'Before = {os.listdir(\"files\")}')\n", - "\n", - "file_path = os.path.join('files', 'mkdir_example_parent', 'mkdir_example_child')\n", - "os.mkdir(file_path)\n", - "\n", - "print(f'After = {os.listdir(\"files\")}')" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "PIZb3CQxDWyC" - }, - "source": [ - "Тук получаваме грешка - `os.mkdir` не може да създаде несъществуващите директории над последната. За целта трябва да използваме метода `os.makedirs`. Той приема отново като аргумент пътят към директорията, която искаме да създадем, както и права на директорията/директориите. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "P0I8cNkEyBUW", - "outputId": "3e378dbd-8016-484d-b6a3-296aee42b881" - }, - "outputs": [], - "source": [ - "import os\n", - "\n", - "print(f'Before = {os.listdir(\"files\")}')\n", - "\n", - "file_path = os.path.join('files', 'mkdir_example_parent', 'mkdir_example_child')\n", - "os.makedirs(file_path)\n", - "\n", - "print(f'After = {os.listdir(\"files\")}')" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "1EDMtz9N1YN6" - }, - "source": [ - "Един допълнителен аргумент, който `makedirs` приема, е аргумента `exist_ok`. Той контролира дали да се хвърли грешка, ако крайната директория която искаме да създадем, вече съществува. " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "NHJPuuq_NBV5" - }, - "source": [ - "### Преместване на директория" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "bs_Hrj42yaYx" - }, - "source": [ - "Освен за преместване на файлове, `os.rename` работи и за директории. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "WjInQ5BRyeGq", - "outputId": "904d46ff-3b3a-41f8-a4eb-2eb90d999f0a" - }, - "outputs": [], - "source": [ - "import os\n", - "\n", - "file_path = os.path.join('files', 'rename_dir_example_1')\n", - "os.makedirs(file_path)\n", - "\n", - "print(f'Before = {os.listdir(\"files\")}')\n", - "\n", - "target_file_path = os.path.join('files', 'renamed_dir_example_1')\n", - "os.rename(file_path, target_file_path)\n", - "print(f'After = {os.listdir(\"files\")}')" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "miLCHiM4OE_n" - }, - "source": [ - "Както споменахме по-горе, ако дестинацията съществува, под Windows `os.rename` хвърля `FileExistsError`. \n", - "\n", - "Под Unix системи (Linux, MacOS) поведението е малко по-различно:\n", - "- Ако source пътя е файл, а destination пътя е директория, се хвърля `IsADirectory` грешка\n", - "- Ако source пътя е директория, а destination пътя е файл, се хвърля `NotADirectory` грешка\n", - "- Ако destination е съществуваща директория:\n", - " - Ако е празна, преместването е успешно\n", - " - Ако не е празна, се хвърля `OSError` грешка\n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "vgkfZTpuNGlv" - }, - "source": [ - "### Изтриване на директория" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "TI6k87QW0d7p" - }, - "source": [ - "Изтриването на директория става чрез функцията `os.rmdir`. Тя приема пътя към директорията, която трябва да бъде изтрита. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "nyr05CrHDAeu", - "outputId": "f58c1bc7-7d69-4115-a532-fb3eaa0ace28" - }, - "outputs": [], - "source": [ - "import os\n", - "\n", - "file_path = os.path.join('files', 'remove_dir_example_1')\n", - "os.makedirs(file_path)\n", - "\n", - "print(f'Before = {os.listdir(\"files\")}')\n", - "\n", - "os.rmdir(file_path)\n", - "print(f'After = {os.listdir(\"files\")}')" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "KboM7dmEDMKW" - }, - "source": [ - "Ако директорията, която се опитаме да изтрием, не е празна, ще получим `OSError`. А ако тя не съществува, ще получим `FileNotFoundError`. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 221 - }, - "id": "0UQyFZKXDgmg", - "outputId": "634b3214-5810-44f4-d55e-0ae51eb62c89" - }, - "outputs": [], - "source": [ - "import os\n", - "\n", - "non_existing_dir = os.path.join('files', 'non_existing_dir')\n", - "\n", - "os.rmdir(non_existing_dir)\n", - "os.rmdir('files')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 204 - }, - "id": "mYec3cULDqNf", - "outputId": "6f6c6dae-f0e3-4182-c901-9d795ba84d43" - }, - "outputs": [], - "source": [ - "import os\n", - "\n", - "os.rmdir('files')" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "4YKfS67MOHJF" - }, - "source": [ - "Освен `os.rmdir`, Python предлага и друга функция за изтриване на директории - `os.removedirs`. Тя обаче работи по специфичен начин. `os.removedirs` приема път до директория, като освен да премахва последната директория от пътя, функцията се опитва да премахне и всички празни директории нагоре, като спира при първата директория, която не е успяла да премахне успешно." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "gPQjlwwxSKEt" - }, - "source": [ - "Нека за целта създадем следната структура - `files/remove_dirs_example` ще е основната ни директория. В нея ще имаме две поддиректории - `first` и `second`. В `first` ще създадем още няколко поддиректории, като всичките ще са празни. В `second` ще създадем един файл и една поддиректория." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "vaRS8bxeTL9j" - }, - "source": [ - "Ще се опитаме да извикаме `os.removedirs` върху най-долната директории в `first` и `second`" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "wOIjU6GCSen3", - "outputId": "16803413-684b-4789-e369-6aeb56b17232" - }, - "outputs": [], - "source": [ - "import os\n", - "\n", - "main_dir = os.path.join('files', 'remove_dirs_example')\n", - "\n", - "first_dir = os.path.join(main_dir, 'first')\n", - "empty_first_subdirs = os.path.join(first_dir, 'a', 'b', 'c')\n", - "\n", - "second_dir = os.path.join(main_dir, 'second')\n", - "empty_second_subdir = os.path.join(second_dir, 'd')\n", - "empty_second_dir_file = os.path.join(second_dir, 'e')\n", - "\n", - "os.makedirs(empty_first_subdirs)\n", - "os.makedirs(empty_second_subdir)\n", - "\n", - "\n", - "fd = open(empty_second_dir_file, 'w')\n", - "fd.write('Hi')\n", - "fd.close()\n", - "\n", - "print(f'Before = {os.listdir(main_dir)}')\n", - "\n", - "os.removedirs(empty_first_subdirs)\n", - "os.removedirs(empty_second_subdir)\n", - "\n", - "print(f'After = {os.listdir(main_dir)}')" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "Z6cWgSYgTyLn" - }, - "source": [ - "Как работи `os.removedirs` в този случай ?\n", - "\n", - "Върху `files/first/a/b/c`:\n", - "1. Опитваме да изтрием `c` => тя е празна, изтрива се\n", - "2. Опитваме да изтрием `b` => тя е празна, изтрива се\n", - "3. Опитваме да изтрием `a` => тя е празна, изтрива се\n", - "4. Опитваме да изтрием `first` => тя е празна, изтрива се\n", - "5. Опитваме да изтрием `remove_dirs_example` => тя не е празна, спираме\n", - "\n", - "Върху `files/second/d`:\n", - "1. Опитваме да изтрием `d` => тя е празна, изтрива се\n", - "2. Опитваме да изтрием `second` => тя не е празна, спираме" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "5YyMwXCQUf9w" - }, - "source": [ - "`os.removedirs` е много подходяща когато имаме много празни директории една в друга, но е редно да се използва внимателно." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "SsKFjwI0U87d" - }, - "source": [ - "За любопитните: Съществува функция [`rmtree`](https://docs.python.org/3.10/library/shutil.html#shutil.rmtree), намираща се в `shutil` библиотеката, която изтрива папка и всички в нея. Нея няма да я разглеждаме в курса." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "BC9RWXG1NX7R" - }, - "source": [ - "### Обща работа с пътища" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "w3SgN35oNwF2" - }, - "source": [ - "В началото видяхме, че за да построим един път до файл или директория в Python, трябва да използваме `os.path.join`. Освен `join`, [`os.path`](https://docs.python.org/3.10/library/os.path.html) предлага много други полезни функции за работа с пътища. \n", - "\n", - "Ще се спрем върху една част от тях, която е по-вероятно да използвате във всекидневната си работа с Python:\n", - "\n", - "- os.path.exists - проверка дали пътят сочи към валиден файл или директория\n", - "- os.path.isdir - дали пътят сочи към валидна директория\n", - "- os.path.isfile - дали пътят сочи към валиден файл\n", - "- os.path.split - отделя последната част от път" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "OWaoBdJ6fuIU", - "outputId": "2ee8ae7e-fd68-48cc-b59e-44d9104f5745" - }, - "outputs": [], - "source": [ - "import os\n", - "print(f'Does the files directory exist: {os.path.exists(\"files\")}')\n", - "print(f'Does the files/lorem_ipsum.txt file exist: {os.path.exists(os.path.join(\"files\", \"lorem_ipsum.txt\"))}')\n", - "print(f'Does the files/ala-bala.txt file exist: {os.path.exists(os.path.join(\"files\", \"ala-bala.txt\"))}')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "vf5udnCGgY_x", - "outputId": "8658b735-05d0-4995-e925-c24edcf88ca6" - }, - "outputs": [], - "source": [ - "import os\n", - "print(f'Is \"files\" a directory: {os.path.isdir(\"files\")}')\n", - "print(f'Is \"files/lorem_ipsum.txt\" a directory: {os.path.isdir(os.path.join(\"files\", \"lorem_ipsum.txt\"))}')\n", - "print(f'Is \"files/ala-bala.txt\" a directory: {os.path.isdir(os.path.join(\"files\", \"ala-bala.txt\"))}') # Although the file does not exist, isdir returns False" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "bWAE_SBmgntD", - "outputId": "3c71363c-8e4e-4762-f25e-639a0fce9023" - }, - "outputs": [], - "source": [ - "import os\n", - "print(f'Is \"files\" a file: {os.path.isfile(\"files\")}')\n", - "print(f'Is \"files/lorem_ipsum.txt\" a file: {os.path.isfile(os.path.join(\"files\", \"lorem_ipsum.txt\"))}')\n", - "print(f'Is \"files/ala-bala.txt\" a directory: {os.path.isfile(os.path.join(\"files\", \"ala-bala.txt\"))}') # Although the file does not exist, isfile returns False" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "rTQnNvzlh0PD" - }, - "source": [ - "`os.path.split` отделя последната част от пътя от останалия. Връща ни наредена двойка от `head` и `tail`, където `tail` съдържа последната част от пътя, а `head` останалото " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "ezd7ZHB5g9fj", - "outputId": "942ab255-cac8-4d8c-d491-a74790536b44" - }, - "outputs": [], - "source": [ - "import os\n", - "\n", - "long_path = os.path.join('/', 'foo', 'bar', 'baz')\n", - "print(f'long_path = {long_path}')\n", - "\n", - "print(f'long_path splitted: {os.path.split(long_path)}')" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "Va2Hb5YhuKHa" - }, - "source": [ - "Важно е да отбележим няколко по-специални случая:\n", - "\n", - "- Ако пътят е празен, `split` ще ни върне празни `head` и `tail`\n", - "- Ако пътят завършва с `/` (или `\\` под Windows), `tail` ще е празен низ\n", - "- Ако пътят не съдържа `/`(или `\\` под Windows), `head` ще е празен низ" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "yCoiMcrM7es1", - "outputId": "ac887a28-898e-4ace-da7f-b756880e245b" - }, - "outputs": [], - "source": [ - "empty_path = ''\n", - "print(f'split with empty path returns = {os.path.split(empty_path)}')\n", - "\n", - "ending_with_separator = '/foo/bar/'\n", - "print(f'split with path ending in / returns = {os.path.split(ending_with_separator)}')\n", - "\n", - "no_separators = 'foo'\n", - "print(f'split with no separators returns = {os.path.split(no_separators)}')" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "8ZE83h4DQLcq" - }, - "source": [ - "### Обхождане на директории" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "L722LFr0QOE5" - }, - "source": [ - "Понякога искаме да обходим цялото директорийно дърво отдадена точка надолу. Python ни позволява да направи това сравнително лесно, с помощта на функцията `os.walk`. \n", - "\n", - "Тя приема като аргумент директорията, от което започва обхождането. Като резултат, тя ни връща генератор съдържащ името на текущата директория, имената на директориите в текущата директория и имената на файловете в текущата директория.\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "Uj_Je3-GRFU6", - "outputId": "e263fc62-0d23-46a0-cae5-b7b33a0875db" - }, - "outputs": [], - "source": [ - "import os\n", - "\n", - "# Setup\n", - "example_root_dir = os.path.join('files', 'walk_example')\n", - "\n", - "d_dir = os.path.join(example_root_dir, 'a', 'b', 'c', 'd')\n", - "os.makedirs(d_dir, exist_ok=True)\n", - "\n", - "os.makedirs(os.path.join(example_root_dir, 'a', 'b1'), exist_ok=True)\n", - "\n", - "b2_dir = os.path.join(example_root_dir, 'a', 'b2')\n", - "os.makedirs(b2_dir, exist_ok=True)\n", - "\n", - "os.makedirs(os.path.join(example_root_dir, 'a', 'b', 'c', 'd1'), exist_ok=True)\n", - "os.makedirs(os.path.join(example_root_dir, 'a', 'b', 'c', 'd2'), exist_ok=True)\n", - "\n", - "files_for_d = [os.path.join(d_dir, f'file{i}') for i in range(5)]\n", - "\n", - "for file_for_d in files_for_d:\n", - " with open(file_for_d, 'w') as fp:\n", - " fp.write(f'Content for {file_for_d}')\n", - "\n", - "files_for_b2 = [os.path.join(d_dir, f'file{i}') for i in range(3)]\n", - "\n", - "for file_for_b2 in files_for_b2:\n", - " with open(file_for_b2, 'w') as fp:\n", - " fp.write(f'Content for {file_for_b2}')\n", - "\n", - "\n", - "# os.walk\n", - "\n", - "for dirname, subdirs, files in os.walk(example_root_dir):\n", - " print(f'In {dirname}, which has subdirs: {subdirs} and files: {files}')" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "FzOWCerhbNUN" - }, - "source": [ - "## Използване на `with`" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "H7dxt9Ma2oho" - }, - "source": [ - "Досега в работата ни с файлове, забелязахме че трябва да подсигурим, че сме затворили файла. Проблемът идва, когато трябва да се справим с грешки, които могат да бъда хвърлени от различните операции с файлове. Дори и при хвърлена грешка, ние все пак трябва да си затворим файла.\n", - "\n", - "Python ни позволява два начина да се справим с този проблем - `try/finally` и `with`." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "QUT2FQfW4Dhk" - }, - "source": [ - "След всеки `try` блок, може да поставим един друг блок, който да се изпълнява винаги след `try` или `except` блоковете. Този допълнителен блок се казва `finally`. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 378 - }, - "id": "diBiYJdp4UEp", - "outputId": "fb210fd8-0112-487f-e884-5fb2674fe1d6" - }, - "outputs": [], - "source": [ - "def raiser():\n", - " raise ValueError('Hello there')\n", - "\n", - "try:\n", - " raiser()\n", - "finally:\n", - " print('After the exception, this will be printed')" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "8ubdugN44sR0" - }, - "source": [ - "По подобен начин можем да използваме `finally` за да затворим отворения файл." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "cnd3mHOI4ruV", - "outputId": "e38cfed0-1ff2-4a26-cb19-830b2d577d98" - }, - "outputs": [], - "source": [ - "import os\n", - "fp = open(os.path.join('files', 'lorem_ipsum.txt'))\n", - "try:\n", - " print(fp.read())\n", - "finally:\n", - " fp.close()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "AOGQnyg65H9d" - }, - "source": [ - "Така, дори и да получим грешка при четене (или каквато и да е друга операция в `try` блока), файла ще бъде затворен." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "qvu90-kN5oCy" - }, - "source": [ - "От тук можем да стигнем до заключението - при работата с ресурси, имаме процес по отваряне и процес по затваряне (независимо от грешки). Python ни предлага и друга синтактична конструкция за работа с такива структури - `with`." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "GpkO14Bw6Tue" - }, - "source": [ - "`with` ни позволява отварянето на нов контекст. В него може да отворим ресурс, а след излизането от блока, този ресурс се затваря автоматично. \n", - "\n", - "Общият вид на `with` е следния:\n", - "```python\n", - "with as :\n", - " f(variable)\n", - "```\n", - "\n", - "Ако искаме да отворим файл и да прочетем нещо с `with`, кода би изглеждал по следния начин:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "NXfepH8q7aLH", - "outputId": "44bea744-9a30-4e8c-f33a-b406f0211c61" - }, - "outputs": [], - "source": [ - "import os\n", - "with open(os.path.join('files', 'lorem_ipsum.txt')) as fp:\n", - " print(fp.read())\n", - "\n", - "print(f'Is the file closed ? {fp.closed}')" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "EGihrAlq739A" - }, - "source": [ - "Дори и при грешка, файлът ще бъде затворен. Ако искаме обаче да хванем грешката, все пак трябва да изпозлваме `try/catch`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "Gg93e1tN8Yeo", - "outputId": "48fb19db-f19d-42d0-fc1a-e11505055301" - }, - "outputs": [], - "source": [ - "import os\n", - "\n", - "try:\n", - " with open(os.path.join('files', 'lorem_ipsum.txt')) as fp:\n", - " print(fp.read())\n", - "except OSError as err:\n", - " print(err)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "8r0zhhbi9Pl8" - }, - "source": [ - "Начинът по който `with` работи е, че извиква два специални магически метода - `__enter__` и `__exit__`, който се изпълняват при влизане и излизане съответно от контекст мениджъра. \n", - "\n", - "По-конкретно, `__enter__` метода се изивиква в `as` частта на `with`. В него връщаме обекта, който ще бъде присвоен на променливата след `as`.\n", - "\n", - "\n", - "Файловите обекти имплементират `__exit__` метода, където затварят отворения файл." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "mItDZ8Bb-jV2" - }, - "source": [ - "Ако искаме и нашите класове да работят с контекстните мениджъри, трябва да имплементираме `__enter__` и `__exit__` методите. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "d6PErO_q-vza" - }, - "outputs": [], - "source": [ - "class ContextableClass:\n", - " def __init__(self, some_var=42):\n", - " self.some_var = some_var\n", - " \n", - " def __enter__(self):\n", - " print('Entering the context manager')\n", - " return self\n", - " \n", - " def __exit__(self, exc_type, exc_value, traceback):\n", - " print('Exiting the context manager')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "QwMZX5jF_2AX", - "outputId": "2cedd113-6b2a-4a64-9ea3-54b7e66f7aef" - }, - "outputs": [], - "source": [ - "with ContextableClass(5) as instance:\n", - " print(instance.some_var)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "BsHLVveUPgaD" - }, - "source": [ - "## `tell` и `seek`" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "CGJR7E48yy6F" - }, - "source": [ - "Писането и четенето от файлове всъщност се осъществява символ по символ. Така например за да запишем 'hello', трябва първо да запишем `h`, после `e` и т.н. \n", - "\n", - "Както при писането с клавиатура имаме позиция върху която пишем, така и при работата с файлове имаме позиция, на която четем или пишем.\n", - "\n", - "Python ни позволява да вземем текущата позиция за четене/писане, както и да преместим тази позиция на друго място. " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "CenG5LURMvZI" - }, - "source": [ - "Можем да вземем позиция на курсора във файла с помощта на метода `tell`. Той ще ни върне като резултат цяло число, символизиращо броя символи след началото на файла." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "n8djitSyM7Kh", - "outputId": "c9809392-5136-46db-b136-c76a5d4a8667" - }, - "outputs": [], - "source": [ - "import os\n", - "\n", - "file_path = os.path.join('files', 'tell_exampe.txt')\n", - "\n", - "# First lets create the file\n", - "with open(file_path, 'w') as fp:\n", - " fp.write('abc\\n')\n", - " fp.write('def\\n')\n", - " fp.write('ghi\\n')\n", - "\n", - "# Now lets read \n", - "with open(file_path) as fp:\n", - " print(f'Current position = {fp.tell()}')\n", - " print(f'Reading a line... {fp.readline()}')\n", - "\n", - " print(f'Current position = {fp.tell()}')\n", - " print(f'Reading another line... {fp.readline()}')\n", - "\n", - " print(f'Current position = {fp.tell()}')" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "iGIdJZIGOIUL" - }, - "source": [ - "Можем да зададем позиция на курсора във файла с помощта на метода `seek`. Той приема два аргумента - какво отместване (`offset`) да направим и от къде (`whence`).\n", - "\n", - "`whence` приема три стойност:\n", - "- 0 (или `os.SEEK_SET`) - означава спрямо началото на файла\n", - "- 1 (или `os.SEEK_CUR`) - означава спрямо текущата позиция\n", - "- 2 (или `os.SEEK_END`) - означава спрямо края на файла\n", - "\n", - "При работа с текстови файлове, отместването е само спрямо началото на файла. Можем единствено да преместим курсора до края на файла с `seek(0, 2)`" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "T3PRQ_lfOjK2", - "outputId": "96db36eb-4703-4be3-e22a-d178eea27e14" - }, - "outputs": [], - "source": [ - "import os\n", - "\n", - "file_path = os.path.join('files', 'seek_exampe.txt')\n", - "\n", - "# First lets create the file\n", - "with open(file_path, 'w') as fp:\n", - " fp.write('abcdefghijk')\n", - "\n", - "with open(file_path, 'r+') as fp:\n", - " fp.seek(2)\n", - " fp.write('!')\n", - "\n", - " fp.seek(0, 2)\n", - " fp.write('@')\n", - "\n", - "with open(file_path) as fp:\n", - " print(fp.read())" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "tjL0TPsTPn0a" - }, - "source": [ - "Бележка: Ако бяхте отворили файла за писане с режим `a`, щяхме да можем да пишем само в края. Режим `r+` ни дава права за четене и пренаписване. " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "YfLvF6IvlDpa" - }, - "source": [ - "## Примери" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "GQFz51abh-L-" - }, - "source": [ - "### Пример 1" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "VjoN0y7etjP4" - }, - "source": [ - "Напишете функция `split_path`, която приема път (като низ), и го разделя на съставните му части. Използвайте `os.path.split`." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "L0gvQsq5txfw" - }, - "source": [ - "#### Решение на пример 1" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "0Ys1jhNfty_0" - }, - "outputs": [], - "source": [ - "import os\n", - "from typing import List\n", - "\n", - "def split_path(file_path: str) -> List[str]:\n", - " head, tail = os.path.split(file_path)\n", - "\n", - " result = [tail]\n", - " while head != '' and head != file_path:\n", - " file_path = head\n", - " head, tail = os.path.split(file_path)\n", - " result.append(tail)\n", - "\n", - " result.reverse()\n", - " return result" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "RhdgDKkna_mh", - "outputId": "f9447561-7d4e-4eb2-c1f3-39dbb4bbef0b" - }, - "outputs": [], - "source": [ - "print(split_path('/foo/bar/baz'))\n", - "print(split_path('/foo/bar/baz/'))\n", - "print(split_path('/foo'))\n", - "print(split_path('foo'))" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "ije_Z0fEXgKM" - }, - "source": [ - "### Пример 2" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "3d7BwPi4Xhtb" - }, - "source": [ - "Даден е клас `Person`, който съдържа информация за човек - неговите имена, рожденна дата, възраст и работа." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "3lCnmwaTXwbe" - }, - "outputs": [], - "source": [ - "class Person:\n", - " def __init__(self, first_name: str='', last_name: str='', birthdate: str='', age: int=0, job: str=''):\n", - " self.__first_name = first_name\n", - " self.__last_name = last_name\n", - " self.__birthdate = birthdate\n", - " self.__age = age\n", - " self.__job = job\n", - "\n", - " @property\n", - " def first_name(self) -> str:\n", - " return self.__first_name\n", - " \n", - " @property\n", - " def last_name(self) -> str:\n", - " return self.__last_name\n", - " \n", - " @property\n", - " def birthdate(self) -> str:\n", - " return self.__birthdate\n", - "\n", - " @property\n", - " def age(self) -> str:\n", - " return self.__age\n", - "\n", - " @property\n", - " def job(self) -> str:\n", - " return self.__job" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "GaGWV_nZYR2-" - }, - "source": [ - "Напишете клас `PersonSerializer`, съдържащ следните методи:\n", - "- Метод, който приема обект от тип `Person` и път. Методът записва във файл информацията за човека\n", - "- Метод, който приема път и връща обект от тип `Person`, който е създаден на база данните от файла\n", - "- Метод, който приема списък от `Person` и път. Методът записва във файл информация за хората\n", - "- Метод, който приема път и връща списък от `Person`, които са създадени на база на информацията във файла\n", - "\n", - "При грешка при писането или четенето класът да хвърля `ValueError`. \n", - "\n", - "Може да приемем, че информацията за един човек ще бъде записана на един ред. " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "TTMqcvL5ZbYj" - }, - "source": [ - "#### Решение на пример 2" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "fQBxLoESZeQV" - }, - "outputs": [], - "source": [ - "from typing import List\n", - "\n", - "class Person:\n", - " def __init__(self, first_name: str='', last_name: str='', birthdate: str='', age: int=0, job: str=''):\n", - " self.__first_name = first_name\n", - " self.__last_name = last_name\n", - " self.__birthdate = birthdate\n", - " self.__age = age\n", - " self.__job = job\n", - "\n", - " @property\n", - " def first_name(self) -> str:\n", - " return self.__first_name\n", - " \n", - " @property\n", - " def last_name(self) -> str:\n", - " return self.__last_name\n", - " \n", - " @property\n", - " def birthdate(self) -> str:\n", - " return self.__birthdate\n", - "\n", - " @property\n", - " def age(self) -> str:\n", - " return self.__age\n", - "\n", - " @property\n", - " def job(self) -> str:\n", - " return self.__job\n", - "\n", - " # Adding a __str__ magic method helps us with printing the info\n", - " def __str__(self) -> str:\n", - " return f'{self.first_name}, {self.last_name}, {self.birthdate}, {self.age}, {self.job}'\n", - " \n", - " @staticmethod\n", - " def from_string(person_info: str):\n", - " first_name, last_name, birthdate, age, job = person_info.strip().split(', ')\n", - " return Person(first_name, last_name, birthdate, age, job)\n", - " \n", - "class PersonSerializer:\n", - " @staticmethod\n", - " def save_person(person: Person, filepath: str, mode: str='w'):\n", - " with open(filepath, mode) as fp:\n", - " fp.write(str(person) + '\\n')\n", - " \n", - " @staticmethod\n", - " def create_person_from_file(filepath: str) -> Person:\n", - " if not os.path.exists(filepath):\n", - " raise ValueError(f'File not found at {filepath}')\n", - " \n", - " with open(filepath) as fp:\n", - " person_info = fp.read()\n", - " return Person.from_string(person_info)\n", - "\n", - " @staticmethod\n", - " def save_people(people: List[Person], filepath: str):\n", - " for person in people:\n", - " PersonSerializer.save_person(person, filepath, mode='a')\n", - " \n", - " @staticmethod\n", - " def create_people_from_file(filepath: str) -> List[Person]:\n", - " if not os.path.exists(filepath):\n", - " raise ValueError(f'File not found at {filepath}')\n", - " \n", - " with open(filepath) as fp:\n", - " return [Person.from_string(person_info) for person_info in fp]\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "VGnQEoPnbcra", - "outputId": "e7a62425-76ec-4a98-d385-42db374f7494" - }, - "outputs": [], - "source": [ - "me = Person('Lyubo', 'Karev', '20-09-1998', 24, 'Developer')\n", - "my_filepath = os.path.join('files', 'lyubo.txt')\n", - "\n", - "PersonSerializer.save_person(me, my_filepath)\n", - "me_again = PersonSerializer.create_person_from_file(my_filepath) # Hello me, meet the real me\n", - "\n", - "print(me)\n", - "print(me_again)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "aju9NHCLcfBQ", - "outputId": "694cc2d1-f2af-44b9-d7fd-4c6e0913cd7a" - }, - "outputs": [], - "source": [ - "bunch_of_people = [\n", - " Person('Lyubo', 'Karev', '20-09-1998', 24, 'Developer'),\n", - " Person('Alex', 'Ignatov', '22-10-1998', 24, 'Developer'),\n", - " Person('Ivan', 'Luchev', '28-04-1998', 24, 'Student')\n", - "]\n", - "\n", - "our_filepath = os.path.join('files', 'us.txt')\n", - "\n", - "PersonSerializer.save_people(bunch_of_people, our_filepath)\n", - "us_again = PersonSerializer.create_people_from_file(our_filepath)\n", - "\n", - "print('Bunch of people:')\n", - "for person in bunch_of_people: \n", - " print(person)\n", - "\n", - "print('Us again:')\n", - "for person in us_again: \n", - " print(person)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Пример 3" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Както споменахме по-горе, `writelines` не добавя автоматично нови редове след всеки подаден елемент от списъка.\n", - "\n", - "Напишете функция `better_writelines`, която поправя тази грешка." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Решение на Пример 3" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "def better_writelines(lines: list[str], filepath: str):\n", - " with open(filepath, 'w') as fp:\n", - " fp.writelines([line + '\\n' for line in lines])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Пример 4" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`os.rmdir` изтрива директория, само ако е празна. \n", - "\n", - "Напишете функция `rm_rf`, която по подадена директория, изтрива съдържанието ѝ, както и самата директория." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Решение на Пример 4" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def rm_rf(filepath: str):\n", - " if os.path.isdir(filepath):\n", - " for file in os.listdir(filepath):\n", - " rm_rf(os.path.join(filepath, file))\n", - " os.rmdir(filepath)\n", - " else:\n", - " os.remove(filepath)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Пример 5" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Напишете програма за управление на музикални файлове. \n", - "\n", - "Програмата трябва да поддържа следните функционалности:\n", - "- Задаване на работна директория (директория, в която са ни музикалните файлове)\n", - "- Извеждане на списък с всички песни (приемете, че всеки файл ще е с име във формат `artist-song_name.mp3`\n", - "- Търсене на песен по изпълнител (връща точно съвпадение)\n", - "- Търсене на псене по име на песен (връща точно съвпадение)\n", - "\n", - "Потребителския интефейс може да бъде следния:\n", - "- Потребителя има достъп до 5 команди (`set`, `list`, `find artist`, `find song` и `exit`)\n", - "- Потребителя може да въвежда (и изпълнява) команди, до въвеждане на `exit`, при което програмата спира работа.\n", - "(Ако имате други идеи за интерфейса, също ще бъдат приети)\n", - "\n", - "Примерни песни може да свалите от [тук](https://drive.google.com/file/d/1lqRxlPHd0THds_WU6l5DsRie0-yoCXFc/view?usp=sharing)" - ] + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "YIgCCwN5a39O" + }, + "source": [ + "# Работа с файлове в Python\n", + "План на лекцията:\n", + "- Кратка интродукция за работата с пътища, Windows и Unix\n", + "- Представяне на файловете в Python\n", + "- Четене на файлове\n", + "- Писане на файлове\n", + "- Работа с файлове и пътища\n", + "- Използване на `with`\n", + "- `tell` и `seek`\n", + "- Примери\n", + "- Задачи" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "LnxJAYYb63dN" + }, + "source": [ + "### Предварителна подготовка " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "L02fnebn7I_E", + "outputId": "d119aaac-5070-42ec-f312-3d3f5515c571" + }, + "outputs": [], + "source": [ + "!mkdir files\n", + "!curl -o files/lorem_ipsum.txt https://raw.githubusercontent.com/lyubolp/PythonCourse2022/08_files/08%20-%20Files/files/1.txt\n", + "!echo \"hello\" > files/hello.txt" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "3Fd8dyDSliLn" + }, + "source": [ + "## Кратка интродукция за работата с пътища, Windows и Unix." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "1FQAdGyXlkjs" + }, + "source": [ + "Път в една файлова система посочва локацията и името на даден обект (бил той файл или директория). Пример за път в Linux/MacOS е `/home/user/myfile.txt`, а в Windows - `C:\\Users\\user\\myfile.txt`. \n", + "\n", + "Забелязва се, че пътищата в Windows и Unix-базираните ОС се разделят с различни черти. Нашият Python код трябва да е съвместим и с двата начина за разделя на пътища. \n", + "\n", + "За наше улеснение, Python предлага функцията `os.path.join`, която по подадени имена на директории/файлове, конструира правилния спрямо нашия OS път." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 36 + }, + "id": "FR2Fp_MrnHDt", + "outputId": "aa05f14d-0ea4-4792-9495-37093431e58d" + }, + "outputs": [], + "source": [ + "import os\n", + "\n", + "os.path.join('/home', 'lyubo', 'myfile.txt')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "RGGzX2_znL_z" + }, + "source": [ + "Понеже кодът е изпълнен под Linux, получаваме Unix-ски път. Ако изпълним обаче същия код под Windows, ще получим правилен Windows-ки път. " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "KPWujZmdo10D" + }, + "source": [ + "## Представяне на файловете в Python" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "FBsGm74Yo6LW" + }, + "source": [ + "Python предоставя API за работа с файлове и потоци. Python работи с т.нар. \"file objects\" - това може да са файлове на диска, sockets, pipes и други потоци.\n", + "\n", + "Освен с текстови файлове, можем да работим и с бинарни файлове. За момента обаче, ще се спрем само върху текстовите файлове." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "e0ME86yzrRuJ" + }, + "source": [ + "Можем да отворим един файл за работа, с помощта на функцията `open`. В най-простия си вид, тя приема път към файл. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "9uztE6pzrYj5", + "outputId": "beeb5370-efd0-4a0b-e148-9b3de6c569c8" + }, + "outputs": [], + "source": [ + "fd = open(os.path.join('files', 'lorem_ipsum.txt'))\n", + "\n", + "print(fd)\n", + "\n", + "fd.close()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "4ohPpwYurg55" + }, + "source": [ + "След като приключим работа с един файл, не трябва да забравяме да го затворим. Затварянето на един файл става с помощта на `close` метода. " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "lpRD3mZarn3x" + }, + "source": [ + "Забелязваме, че обекта който получаваме като резултат от `open` има име (`files/lorem_ipsum.txt`), режим (`r`) и кодиране (`UTF-8`).\n", + "\n", + "Един файл може да бъде отворен в няколко различни режима:\n", + "- `r` - отваря файла за четене\n", + "- `w` - отваря файла за писане, като файла първо бива зачистен\n", + "- `a` - отваря файла за писане, като новото съдържание се записва в края на файла\n", + "- `x` - създава файла, ако не съществува. Ако файла вече съществува, се хвърля `FileExistsError`\n", + "- `b` - отваря файл в бинарен режим\n", + "- `t` - отваря файл в текстови режим\n", + "- `+` - отваря файла за четене и писане\n", + "\n", + "Освен режима на отваряне, можем да променим и кодирането, с което се опитваме да четем файла. По подразбиране, използваме `UTF-8`. " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "RlrURy5tmAHa" + }, + "source": [ + "Ако се опитаме да запишем файл в несъществуваща директория, ще получим грешка" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 239 + }, + "id": "jcixN1mWl_sN", + "outputId": "f028fd68-4a0b-49fb-f998-1bdc8307e1d7" + }, + "outputs": [], + "source": [ + "fd = open(os.path.join('files', 'non_existing_dir', 'new_file.txt'), 'w')\n", + "\n", + "fd.write('content')\n", + "\n", + "fd.close()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "UU_sozqXbJhx" + }, + "source": [ + "## Четене на файлове" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "D27sqq5ilMSp" + }, + "source": [ + "В Python имаме три метода, чрез които можем да четем от файлове: `read`, `readline` и `readlines`. " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "jJNafCFk12bQ" + }, + "source": [ + "Нека първо разгледаме метода `read`. Той прочита целия файл и запазва съдържанието му в променлива, като един голям низ. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "h92FBNCNat7U", + "outputId": "d4ad48b3-f35a-4960-89ec-d3d294d138c3" + }, + "outputs": [], + "source": [ + "import os\n", + "\n", + "fd = open(os.path.join('files', 'lorem_ipsum.txt'))\n", + "\n", + "content = fd.read()\n", + "print(f'content={content}')\n", + "\n", + "fd.close()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "vGKGqRa-1K_m" + }, + "source": [ + "Веднъж прочетен един файл, следващото прочитане ще ни върне празен низ. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "-Xfg_I0NJJ9C", + "outputId": "ef0f129a-d0b4-43c6-b9b3-e22a53d2b6f8" + }, + "outputs": [], + "source": [ + "fd = open(os.path.join('files', 'lorem_ipsum.txt'))\n", + "\n", + "content = fd.read()\n", + "print(f'content={content}')\n", + "\n", + "content = fd.read()\n", + "print(f'content={content}')\n", + "\n", + "fd.close()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ULGoY5x72lCb" + }, + "source": [ + "Друг вариант за четене на файл, е ред по ред - това става с помощта на метода `readline`. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "oK9Xi-Gu2zr7", + "outputId": "d297f349-c3db-4142-b098-f0cc2426f054" + }, + "outputs": [], + "source": [ + "import os\n", + "fd = open(os.path.join('files', 'lorem_ipsum.txt'))\n", + "\n", + "content = fd.readline()\n", + "print(f'content={content}')\n", + "\n", + "content = fd.readline()\n", + "print(f'content={content}')\n", + "\n", + "fd.close()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "_DXlyrPY315L" + }, + "source": [ + "След прочитане на последния ред, `readline` връща празен низ." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "yVRgVthN3vix", + "outputId": "36bfcf22-3204-4e19-d5e3-015309f9efa4" + }, + "outputs": [], + "source": [ + "import os\n", + "fd = open(os.path.join('files', 'lorem_ipsum.txt'))\n", + "\n", + "content = fd.readline()\n", + "print(f'content={content}')\n", + "\n", + "while content != '':\n", + " content = fd.readline()\n", + " print(f'content={content}')\n", + "\n", + "fd.close()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "6nLUpVFq4Ld4" + }, + "source": [ + "Ако искаме да получим списък от всички редове във файл, можем да използваме `readlines`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "xcmi0pcO4ULY", + "outputId": "0fab048c-1f0e-4bf7-bea1-3262dbb79800" + }, + "outputs": [], + "source": [ + "import os\n", + "fd = open(os.path.join('files', 'lorem_ipsum.txt'))\n", + "\n", + "content = fd.readlines()\n", + "print(f'content={content}')\n", + "\n", + "fd.close()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "zPRIvtWb4Ycu", + "outputId": "cba32c68-7e4f-474c-88d9-76a72d75d0ed" + }, + "outputs": [], + "source": [ + "import os\n", + "fd = open(os.path.join('files', 'lorem_ipsum.txt'))\n", + "\n", + "content = fd.readlines()\n", + "\n", + "for line in content:\n", + " print(f'content={line}')\n", + "\n", + "fd.close()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "URkhviux8oQn" + }, + "source": [ + "Вместо да използваме `readlines`, можем да итерираме директно по файл обекта." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "6uiK1ioa9zCq", + "outputId": "e6ee4b1d-9bcb-4f27-ce4c-30bbc78319b5" + }, + "outputs": [], + "source": [ + "import os\n", + "fd = open(os.path.join('files', 'lorem_ipsum.txt'))\n", + "\n", + "for line in fd:\n", + " print(f'content={line}')\n", + "\n", + "fd.close()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "90Il8AMhbLu2" + }, + "source": [ + "## Писане на файлове" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "GUyazAnfSESG" + }, + "source": [ + "За писане на файлове в Python може да използваме методите `write` и `writelines`" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "URaUQ1hVS_Dx" + }, + "source": [ + "Методът `write` записва низ във файла. Позицията зависи от начина по който е отворен файла (с или без изтриване на текущото съдържание)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "epMK7--sUZuH" + }, + "outputs": [], + "source": [ + "import os\n", + "fd = open(os.path.join('files', 'hello_world.txt'), 'w')\n", + "\n", + "fd.write('Hello world')\n", + "\n", + "fd.close()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "J0n45OqOh8D2", + "outputId": "db5e12b5-ca9f-4172-826b-bb7ca61f5c94" + }, + "outputs": [], + "source": [ + "import os\n", + "fd = open(os.path.join('files', 'hello_world.txt'))\n", + "\n", + "content = fd.read()\n", + "print(content)\n", + "fd.close()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "SiZ4QZjZiZOo" + }, + "source": [ + "Припомням, че ако отворим файла в режим `w`, то ще изтрием текущото му съдържание." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "VJPqmvsgiYrZ" + }, + "outputs": [], + "source": [ + "import os\n", + "\n", + "fd = open(os.path.join('files', 'my_important_file.txt'), 'w')\n", + "fd.write('Really important')\n", + "fd.close()\n", + "\n", + "# Oops, forgot to write something down\n", + "\n", + "fd = open(os.path.join('files', 'my_important_file.txt'), 'w')\n", + "fd.write('Should really not forget this')\n", + "fd.close()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "nVBAP46YiwhL", + "outputId": "9c7a298f-d682-4886-8713-fbece2640792" + }, + "outputs": [], + "source": [ + "import os\n", + "fd = open(os.path.join('files', 'my_important_file.txt'))\n", + "\n", + "content = fd.read()\n", + "print(content)\n", + "fd.close()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "wuq58B4vi5Ub" + }, + "source": [ + "Ако искаме да пишем в края на файла, трябва да използваме режим `a`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "bwjQXxlci9Kx" + }, + "outputs": [], + "source": [ + "import os\n", + "\n", + "fd = open(os.path.join('files', 'my_important_file_2.txt'), 'a')\n", + "fd.write('Really important\\n')\n", + "fd.close()\n", + "\n", + "# Oops, forgot to write something down\n", + "\n", + "fd = open(os.path.join('files', 'my_important_file_2.txt'), 'a')\n", + "fd.write('Should really not forget this\\n')\n", + "fd.close()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "sdlaFvC6jJ1x", + "outputId": "fde00371-b2c4-4ff7-97c0-cc2ffce4b43b" + }, + "outputs": [], + "source": [ + "import os\n", + "fd = open(os.path.join('files', 'my_important_file_2.txt'))\n", + "\n", + "content = fd.read()\n", + "print(content)\n", + "fd.close()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "MMU2OzCFjqzI" + }, + "source": [ + "Методът `writelines` приема списък от \"редове\", които да бъдат записани във файла.\n", + "\n", + "**Забележка**: `writelines` не добавя автоматично нови редове след всеки елемент, затова се очаква всеки елемент от списъка да съдържа нов ред в себе си. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Cl81-OtJkvXW" + }, + "outputs": [], + "source": [ + "import os\n", + "\n", + "fd = open(os.path.join('files', 'writelines_example.txt'), 'w')\n", + "\n", + "lines_to_write = ['hello\\n', 'this\\n', 'are\\n', 'my\\n', 'lines\\n']\n", + "fd.writelines(lines_to_write)\n", + "\n", + "fd.close()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "p1P40tosk-5A", + "outputId": "e3c801f9-f824-46c2-d7c1-18ea44faf72e" + }, + "outputs": [], + "source": [ + "import os\n", + "fd = open(os.path.join('files', 'writelines_example.txt'))\n", + "\n", + "content = fd.read()\n", + "print(content)\n", + "fd.close()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "mk0qWw2GWTFP" + }, + "source": [ + "Тук е добре да се отбележи, че можем да записваме и други типове данни, освен низове (е не точно, но...).\n", + "\n", + "Стига даден тип (или обект) да има низово представяне, можем да го запишем във файл." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "E67N2bZvWjxq", + "outputId": "f7a352ca-8287-4bb6-8072-73860b20d11a" + }, + "outputs": [], + "source": [ + "import os\n", + "\n", + "other_types_in_files = os.path.join('files', 'other_types_in_files.txt')\n", + "\n", + "fd = open(other_types_in_files, 'w')\n", + "\n", + "fd.write(str(2) + '\\n')\n", + "fd.write(str([2, 3, 4]) + '\\n')\n", + "fd.write(str({'a': 2, 'b': 3, 'c': 4}) + '\\n')\n", + "fd.write(str((2, 3)) + '\\n')\n", + "\n", + "fd.close()\n", + "\n", + "fd = open(other_types_in_files)\n", + "content = fd.read()\n", + "print(content)\n", + "fd.close()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Nlb97sGOf4s8" + }, + "source": [ + "## Работа с файлове и пътища" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "oe4Ti1UBhsVW" + }, + "source": [ + "Python предлага удобен начин за работа с файлове. Вградената библиотека `os` съдържа всичко необходимо за работата с файлове и директории. \n", + "\n", + "Ще разгледаме как можем да:\n", + "- Прегледаме съдържанието на директория\n", + "- Преместим файл\n", + "- Изтрием файл\n", + "- Създадем директория\n", + "- Преместим директория\n", + "- Изтрием директория\n", + "- Обща работа с пътища\n", + "- Обхождане на директории" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "HbjmmlfMYpVo" + }, + "source": [ + "### Преглеждане на съдържание на директория" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Ks5R8-anYyA-" + }, + "source": [ + "Можем да видим съдържанието на директория като използваме `os.listdir` метода. Той ни връща списък от низове, съдържащи имената на директориите и файловете в исканата от нас папка. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "KMuyJeL_Zw8t", + "outputId": "81420cb0-3fff-475a-83cc-b362f57e7317" + }, + "outputs": [], + "source": [ + "import os\n", + "\n", + "print(os.listdir('files'))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "G3Jqt8kLi8W0" + }, + "source": [ + "### Преместване на файл" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "njIXBgExNpyD" + }, + "source": [ + "Преместването на файл става чрез \"преименуването му\" (или всъщност, преименуването на файл е преместването му като файл с друго име 🤔) - това в Python става с помощта на функцията `os.rename`. Тя приема два аргумента - source път и destination път (т.е. старото и новото име на файла)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "1g2-mwQLOnro", + "outputId": "c6c7367e-ace7-4d91-bbff-30feed94262c" + }, + "outputs": [], + "source": [ + "import os\n", + "\n", + "file_to_be_moved = os.path.join('files', 'to_be_moved.txt')\n", + "\n", + "fp = open(file_to_be_moved, 'w')\n", + "fp.write('This file is to be moved')\n", + "fp.close()\n", + "\n", + "print(f'Before = {os.listdir(\"files\")}')\n", + "\n", + "file_moved = os.path.join('files', 'file_moved.txt')\n", + "\n", + "os.rename(file_to_be_moved, file_moved)\n", + "\n", + "print(f'After = {os.listdir(\"files\")}')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "CKGnXOydcpvy" + }, + "source": [ + "Ако вече съществува файл със същото име се случват едно от две неща:\n", + "- Ако кодът се изпълнява под Windows, се хвърля `FileExistsError`. \n", + "- Ако кодът се изпълнява под Linux/MacOS и имаме права върху файла върху който ще пишем, той ще бъде презаписан" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "qRE0bS-BliKM" + }, + "source": [ + "Нека разгледаме следната ситуация: имаме файл `files/a/file.txt`, който искаме да преместим в `files/b`, но директорията `b` не съществува." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "p3IzgYI0l1Rl" + }, + "outputs": [], + "source": [ + "!mkdir files/a" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 204 + }, + "id": "t7hy8TpPlrsS", + "outputId": "940ab36b-b456-49d1-e9a7-8e37374723ae" + }, + "outputs": [], + "source": [ + "import os\n", + "\n", + "file_to_be_moved = os.path.join('files', 'a', 'file.txt')\n", + "\n", + "fp = open(file_to_be_moved, 'w')\n", + "fp.write('This file is to be moved')\n", + "fp.close()\n", + "\n", + "os.rename(file_to_be_moved, os.path.join('files', 'b', 'file.txt'))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "baI__6ycM8IS" + }, + "source": [ + "### Изтриване на файл" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "TbpItit-NzqO" + }, + "source": [ + "Изтриването на файл се случва чрез метода `os.remove`. Той приема един аргумент - пътят към файла, който ще бъде изтрит." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "MuNNNLMJpXNS", + "outputId": "f31fc9f8-4db8-499f-c6de-9d321cef7907" + }, + "outputs": [], + "source": [ + "import os\n", + "\n", + "file_path = os.path.join('files', 'to_removed.txt')\n", + "fp = open(file_path, 'w')\n", + "fp.write('This file will be deleted')\n", + "fp.close()\n", + "\n", + "print(f'Before = {os.listdir(\"files\")}')\n", + "\n", + "os.remove(file_path)\n", + "\n", + "print(f'After = {os.listdir(\"files\")}')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "4lDTPY-6dsce" + }, + "source": [ + "Ако се опитаме да изтрием директория с `os.remove`, ще получим грешка `IsADirectoryError`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 204 + }, + "id": "7n1HOZdUdzQn", + "outputId": "ed4ade21-2c0a-4a40-bb08-ade1059a8c79" + }, + "outputs": [], + "source": [ + "import os\n", + "\n", + "os.remove('files')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "yzM5ffT8eSVt" + }, + "source": [ + "А ако опитаме да изтрием файл, който не съществува, ще получим грешка `FileNotFoundError`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 204 + }, + "id": "gwHS4rtYeX3f", + "outputId": "af413bc7-5a19-4a8f-bdb6-90de264f714b" + }, + "outputs": [], + "source": [ + "import os\n", + "\n", + "non_existant_path = os.path.join('files', 'this_file_does_not_exist.txt')\n", + "\n", + "os.remove(non_existant_path)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Копиране на файлове" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "В Python можем да копираме файлове с помощта на `shutil` библиотеката. Тя ни предоставя методи за копиране на файлове.\n", + "\n", + "Ще разгледаме част от тях:\n", + "\n", + "- `shutil.copy()` - копира файл от едно място на друго. Приема два аргумента - source и target пътища. Ако файлът вече съществува на новото място, ще бъде презаписан. Ако target е директория, ще се запише копие на файла в тази директория със същото име.\n", + "- `shutil.copy2()` - работи по същия начин като `shutil.copy()`, но запазва и метаданните на файла (например времето на създаване)\n", + "- `shutil.copystat()` - копира само метаданните на файла\n", + "- `shutil.copytree()` - копира директория и всички файлове в нея. Приема два аргумента - source и target пътища. " + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Before = ['1.txt']\n", + "After = ['1_2.txt', '1.txt', '1_1.txt']\n" + ] } - ], - "metadata": { + ], + "source": [ + "import os\n", + "import shutil\n", + "\n", + "print(f'Before = {os.listdir(\"files\")}')\n", + "\n", + "shutil.copy(os.path.join('files', '1.txt'), os.path.join('files', '1_1.txt'))\n", + "shutil.copy2(os.path.join('files', '1.txt'), os.path.join('files', '1_2.txt'))\n", + "\n", + "print(f'After = {os.listdir(\"files\")}')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Ekhha8biM-Ps" + }, + "source": [ + "### Създаване на директория" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "SiGgH-3r_zGq" + }, + "source": [ + "В Python създаването на директория става чрез метода `os.mkdir`. Той приема пътя към директорията, която да бъде създадена. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { "colab": { - "authorship_tag": "ABX9TyN0PNFYqG1/FYzAoG4SAI8S", - "collapsed_sections": [], - "include_colab_link": true, - "provenance": [] - }, - "kernelspec": { - "display_name": "Python 3", - "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.10.12" + "base_uri": "https://localhost:8080/" + }, + "id": "Y8_j9oJHAUA7", + "outputId": "c9c97bf8-accd-42fa-b28c-9dcf4d42dfd4" + }, + "outputs": [], + "source": [ + "import os\n", + "\n", + "print(f'Before = {os.listdir(\"files\")}')\n", + "\n", + "file_path = os.path.join('files', 'mkdir_example')\n", + "os.mkdir(file_path)\n", + "\n", + "print(f'After = {os.listdir(\"files\")}')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "kbA7dS_PAzTr" + }, + "source": [ + "Само за информация: Можем да зададем права на директорията, с помощта на аргумента `mode`. " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "E9bfpXokDlB_" + }, + "source": [ + "Ако се опитаме да създадем директория, която вече съществува, ще получим `FileExistsError`. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 204 + }, + "id": "jUSXZTQCD0v-", + "outputId": "4984bab1-7b64-48b1-bcf1-781960502d79" + }, + "outputs": [], + "source": [ + "import os\n", + "\n", + "file_path = os.path.join('files', 'mkdir_existing_directory')\n", + "os.mkdir(file_path)\n", + "\n", + "os.mkdir(file_path)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "lTXna1gtBs0g" + }, + "source": [ + "Нека опитаме да създадем няколко нови директории, една под друга. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 276 + }, + "id": "sp1qFjZGC81F", + "outputId": "1a2cf982-7fa2-4024-b6a8-1ec473a978ac" + }, + "outputs": [], + "source": [ + "import os\n", + "\n", + "print(f'Before = {os.listdir(\"files\")}')\n", + "\n", + "file_path = os.path.join('files', 'mkdir_example_parent', 'mkdir_example_child')\n", + "os.mkdir(file_path)\n", + "\n", + "print(f'After = {os.listdir(\"files\")}')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "PIZb3CQxDWyC" + }, + "source": [ + "Тук получаваме грешка - `os.mkdir` не може да създаде несъществуващите директории над последната. За целта трябва да използваме метода `os.makedirs`. Той приема отново като аргумент пътят към директорията, която искаме да създадем, както и права на директорията/директориите. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "P0I8cNkEyBUW", + "outputId": "3e378dbd-8016-484d-b6a3-296aee42b881" + }, + "outputs": [], + "source": [ + "import os\n", + "\n", + "print(f'Before = {os.listdir(\"files\")}')\n", + "\n", + "file_path = os.path.join('files', 'mkdir_example_parent', 'mkdir_example_child')\n", + "os.makedirs(file_path)\n", + "\n", + "print(f'After = {os.listdir(\"files\")}')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "1EDMtz9N1YN6" + }, + "source": [ + "Един допълнителен аргумент, който `makedirs` приема, е аргумента `exist_ok`. Той контролира дали да се хвърли грешка, ако крайната директория която искаме да създадем, вече съществува. " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "NHJPuuq_NBV5" + }, + "source": [ + "### Преместване на директория" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "bs_Hrj42yaYx" + }, + "source": [ + "Освен за преместване на файлове, `os.rename` работи и за директории. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "WjInQ5BRyeGq", + "outputId": "904d46ff-3b3a-41f8-a4eb-2eb90d999f0a" + }, + "outputs": [], + "source": [ + "import os\n", + "\n", + "file_path = os.path.join('files', 'rename_dir_example_1')\n", + "os.makedirs(file_path)\n", + "\n", + "print(f'Before = {os.listdir(\"files\")}')\n", + "\n", + "target_file_path = os.path.join('files', 'renamed_dir_example_1')\n", + "os.rename(file_path, target_file_path)\n", + "print(f'After = {os.listdir(\"files\")}')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "miLCHiM4OE_n" + }, + "source": [ + "Както споменахме по-горе, ако дестинацията съществува, под Windows `os.rename` хвърля `FileExistsError`. \n", + "\n", + "Под Unix системи (Linux, MacOS) поведението е малко по-различно:\n", + "- Ако source пътя е файл, а destination пътя е директория, се хвърля `IsADirectory` грешка\n", + "- Ако source пътя е директория, а destination пътя е файл, се хвърля `NotADirectory` грешка\n", + "- Ако destination е съществуваща директория:\n", + " - Ако е празна, преместването е успешно\n", + " - Ако не е празна, се хвърля `OSError` грешка\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "vgkfZTpuNGlv" + }, + "source": [ + "### Изтриване на директория" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "TI6k87QW0d7p" + }, + "source": [ + "Изтриването на директория става чрез функцията `os.rmdir`. Тя приема пътя към директорията, която трябва да бъде изтрита. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "nyr05CrHDAeu", + "outputId": "f58c1bc7-7d69-4115-a532-fb3eaa0ace28" + }, + "outputs": [], + "source": [ + "import os\n", + "\n", + "file_path = os.path.join('files', 'remove_dir_example_1')\n", + "os.makedirs(file_path)\n", + "\n", + "print(f'Before = {os.listdir(\"files\")}')\n", + "\n", + "os.rmdir(file_path)\n", + "print(f'After = {os.listdir(\"files\")}')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "KboM7dmEDMKW" + }, + "source": [ + "Ако директорията, която се опитаме да изтрием, не е празна, ще получим `OSError`. А ако тя не съществува, ще получим `FileNotFoundError`. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 221 + }, + "id": "0UQyFZKXDgmg", + "outputId": "634b3214-5810-44f4-d55e-0ae51eb62c89" + }, + "outputs": [], + "source": [ + "import os\n", + "\n", + "non_existing_dir = os.path.join('files', 'non_existing_dir')\n", + "\n", + "os.rmdir(non_existing_dir)\n", + "os.rmdir('files')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 204 + }, + "id": "mYec3cULDqNf", + "outputId": "6f6c6dae-f0e3-4182-c901-9d795ba84d43" + }, + "outputs": [], + "source": [ + "import os\n", + "\n", + "os.rmdir('files')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "4YKfS67MOHJF" + }, + "source": [ + "Освен `os.rmdir`, Python предлага и друга функция за изтриване на директории - `os.removedirs`. Тя обаче работи по специфичен начин. `os.removedirs` приема път до директория, като освен да премахва последната директория от пътя, функцията се опитва да премахне и всички празни директории нагоре, като спира при първата директория, която не е успяла да премахне успешно." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "gPQjlwwxSKEt" + }, + "source": [ + "Нека за целта създадем следната структура - `files/remove_dirs_example` ще е основната ни директория. В нея ще имаме две поддиректории - `first` и `second`. В `first` ще създадем още няколко поддиректории, като всичките ще са празни. В `second` ще създадем един файл и една поддиректория." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "vaRS8bxeTL9j" + }, + "source": [ + "Ще се опитаме да извикаме `os.removedirs` върху най-долната директории в `first` и `second`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "wOIjU6GCSen3", + "outputId": "16803413-684b-4789-e369-6aeb56b17232" + }, + "outputs": [], + "source": [ + "import os\n", + "\n", + "main_dir = os.path.join('files', 'remove_dirs_example')\n", + "\n", + "first_dir = os.path.join(main_dir, 'first')\n", + "empty_first_subdirs = os.path.join(first_dir, 'a', 'b', 'c')\n", + "\n", + "second_dir = os.path.join(main_dir, 'second')\n", + "empty_second_subdir = os.path.join(second_dir, 'd')\n", + "empty_second_dir_file = os.path.join(second_dir, 'e')\n", + "\n", + "os.makedirs(empty_first_subdirs)\n", + "os.makedirs(empty_second_subdir)\n", + "\n", + "\n", + "fd = open(empty_second_dir_file, 'w')\n", + "fd.write('Hi')\n", + "fd.close()\n", + "\n", + "print(f'Before = {os.listdir(main_dir)}')\n", + "\n", + "os.removedirs(empty_first_subdirs)\n", + "os.removedirs(empty_second_subdir)\n", + "\n", + "print(f'After = {os.listdir(main_dir)}')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Z6cWgSYgTyLn" + }, + "source": [ + "Как работи `os.removedirs` в този случай ?\n", + "\n", + "Върху `files/first/a/b/c`:\n", + "1. Опитваме да изтрием `c` => тя е празна, изтрива се\n", + "2. Опитваме да изтрием `b` => тя е празна, изтрива се\n", + "3. Опитваме да изтрием `a` => тя е празна, изтрива се\n", + "4. Опитваме да изтрием `first` => тя е празна, изтрива се\n", + "5. Опитваме да изтрием `remove_dirs_example` => тя не е празна, спираме\n", + "\n", + "Върху `files/second/d`:\n", + "1. Опитваме да изтрием `d` => тя е празна, изтрива се\n", + "2. Опитваме да изтрием `second` => тя не е празна, спираме" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "5YyMwXCQUf9w" + }, + "source": [ + "`os.removedirs` е много подходяща когато имаме много празни директории една в друга, но е редно да се използва внимателно." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "SsKFjwI0U87d" + }, + "source": [ + "За любопитните: Съществува функция [`rmtree`](https://docs.python.org/3.10/library/shutil.html#shutil.rmtree), намираща се в `shutil` библиотеката, която изтрива папка и всички в нея. Нея няма да я разглеждаме в курса." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "BC9RWXG1NX7R" + }, + "source": [ + "### Обща работа с пътища" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "w3SgN35oNwF2" + }, + "source": [ + "В началото видяхме, че за да построим един път до файл или директория в Python, трябва да използваме `os.path.join`. Освен `join`, [`os.path`](https://docs.python.org/3.10/library/os.path.html) предлага много други полезни функции за работа с пътища. \n", + "\n", + "Ще се спрем върху една част от тях, която е по-вероятно да използвате във всекидневната си работа с Python:\n", + "\n", + "- os.path.exists - проверка дали пътят сочи към валиден файл или директория\n", + "- os.path.isdir - дали пътят сочи към валидна директория\n", + "- os.path.isfile - дали пътят сочи към валиден файл\n", + "- os.path.split - отделя последната част от път" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "OWaoBdJ6fuIU", + "outputId": "2ee8ae7e-fd68-48cc-b59e-44d9104f5745" + }, + "outputs": [], + "source": [ + "import os\n", + "print(f'Does the files directory exist: {os.path.exists(\"files\")}')\n", + "print(f'Does the files/lorem_ipsum.txt file exist: {os.path.exists(os.path.join(\"files\", \"lorem_ipsum.txt\"))}')\n", + "print(f'Does the files/ala-bala.txt file exist: {os.path.exists(os.path.join(\"files\", \"ala-bala.txt\"))}')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "vf5udnCGgY_x", + "outputId": "8658b735-05d0-4995-e925-c24edcf88ca6" + }, + "outputs": [], + "source": [ + "import os\n", + "print(f'Is \"files\" a directory: {os.path.isdir(\"files\")}')\n", + "print(f'Is \"files/lorem_ipsum.txt\" a directory: {os.path.isdir(os.path.join(\"files\", \"lorem_ipsum.txt\"))}')\n", + "print(f'Is \"files/ala-bala.txt\" a directory: {os.path.isdir(os.path.join(\"files\", \"ala-bala.txt\"))}') # Although the file does not exist, isdir returns False" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "bWAE_SBmgntD", + "outputId": "3c71363c-8e4e-4762-f25e-639a0fce9023" + }, + "outputs": [], + "source": [ + "import os\n", + "print(f'Is \"files\" a file: {os.path.isfile(\"files\")}')\n", + "print(f'Is \"files/lorem_ipsum.txt\" a file: {os.path.isfile(os.path.join(\"files\", \"lorem_ipsum.txt\"))}')\n", + "print(f'Is \"files/ala-bala.txt\" a directory: {os.path.isfile(os.path.join(\"files\", \"ala-bala.txt\"))}') # Although the file does not exist, isfile returns False" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "rTQnNvzlh0PD" + }, + "source": [ + "`os.path.split` отделя последната част от пътя от останалия. Връща ни наредена двойка от `head` и `tail`, където `tail` съдържа последната част от пътя, а `head` останалото " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "ezd7ZHB5g9fj", + "outputId": "942ab255-cac8-4d8c-d491-a74790536b44" + }, + "outputs": [], + "source": [ + "import os\n", + "\n", + "long_path = os.path.join('/', 'foo', 'bar', 'baz')\n", + "print(f'long_path = {long_path}')\n", + "\n", + "print(f'long_path splitted: {os.path.split(long_path)}')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Va2Hb5YhuKHa" + }, + "source": [ + "Важно е да отбележим няколко по-специални случая:\n", + "\n", + "- Ако пътят е празен, `split` ще ни върне празни `head` и `tail`\n", + "- Ако пътят завършва с `/` (или `\\` под Windows), `tail` ще е празен низ\n", + "- Ако пътят не съдържа `/`(или `\\` под Windows), `head` ще е празен низ" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "yCoiMcrM7es1", + "outputId": "ac887a28-898e-4ace-da7f-b756880e245b" + }, + "outputs": [], + "source": [ + "empty_path = ''\n", + "print(f'split with empty path returns = {os.path.split(empty_path)}')\n", + "\n", + "ending_with_separator = '/foo/bar/'\n", + "print(f'split with path ending in / returns = {os.path.split(ending_with_separator)}')\n", + "\n", + "no_separators = 'foo'\n", + "print(f'split with no separators returns = {os.path.split(no_separators)}')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "8ZE83h4DQLcq" + }, + "source": [ + "### Обхождане на директории" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "L722LFr0QOE5" + }, + "source": [ + "Понякога искаме да обходим цялото директорийно дърво отдадена точка надолу. Python ни позволява да направи това сравнително лесно, с помощта на функцията `os.walk`. \n", + "\n", + "Тя приема като аргумент директорията, от което започва обхождането. Като резултат, тя ни връща генератор съдържащ името на текущата директория, имената на директориите в текущата директория и имената на файловете в текущата директория.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Uj_Je3-GRFU6", + "outputId": "e263fc62-0d23-46a0-cae5-b7b33a0875db" + }, + "outputs": [], + "source": [ + "import os\n", + "\n", + "# Setup\n", + "example_root_dir = os.path.join('files', 'walk_example')\n", + "\n", + "d_dir = os.path.join(example_root_dir, 'a', 'b', 'c', 'd')\n", + "os.makedirs(d_dir, exist_ok=True)\n", + "\n", + "os.makedirs(os.path.join(example_root_dir, 'a', 'b1'), exist_ok=True)\n", + "\n", + "b2_dir = os.path.join(example_root_dir, 'a', 'b2')\n", + "os.makedirs(b2_dir, exist_ok=True)\n", + "\n", + "os.makedirs(os.path.join(example_root_dir, 'a', 'b', 'c', 'd1'), exist_ok=True)\n", + "os.makedirs(os.path.join(example_root_dir, 'a', 'b', 'c', 'd2'), exist_ok=True)\n", + "\n", + "files_for_d = [os.path.join(d_dir, f'file{i}') for i in range(5)]\n", + "\n", + "for file_for_d in files_for_d:\n", + " with open(file_for_d, 'w') as fp:\n", + " fp.write(f'Content for {file_for_d}')\n", + "\n", + "files_for_b2 = [os.path.join(d_dir, f'file{i}') for i in range(3)]\n", + "\n", + "for file_for_b2 in files_for_b2:\n", + " with open(file_for_b2, 'w') as fp:\n", + " fp.write(f'Content for {file_for_b2}')\n", + "\n", + "\n", + "# os.walk\n", + "\n", + "for dirname, subdirs, files in os.walk(example_root_dir):\n", + " print(f'In {dirname}, which has subdirs: {subdirs} and files: {files}')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "FzOWCerhbNUN" + }, + "source": [ + "## Използване на `with`" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "H7dxt9Ma2oho" + }, + "source": [ + "Досега в работата ни с файлове, забелязахме че трябва да подсигурим, че сме затворили файла. Проблемът идва, когато трябва да се справим с грешки, които могат да бъда хвърлени от различните операции с файлове. Дори и при хвърлена грешка, ние все пак трябва да си затворим файла.\n", + "\n", + "Python ни позволява два начина да се справим с този проблем - `try/finally` и `with`." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "QUT2FQfW4Dhk" + }, + "source": [ + "След всеки `try` блок, може да поставим един друг блок, който да се изпълнява винаги след `try` или `except` блоковете. Този допълнителен блок се казва `finally`. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 378 + }, + "id": "diBiYJdp4UEp", + "outputId": "fb210fd8-0112-487f-e884-5fb2674fe1d6" + }, + "outputs": [], + "source": [ + "def raiser():\n", + " raise ValueError('Hello there')\n", + "\n", + "try:\n", + " raiser()\n", + "finally:\n", + " print('After the exception, this will be printed')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "8ubdugN44sR0" + }, + "source": [ + "По подобен начин можем да използваме `finally` за да затворим отворения файл." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "cnd3mHOI4ruV", + "outputId": "e38cfed0-1ff2-4a26-cb19-830b2d577d98" + }, + "outputs": [], + "source": [ + "import os\n", + "fp = open(os.path.join('files', 'lorem_ipsum.txt'))\n", + "try:\n", + " print(fp.read())\n", + "finally:\n", + " fp.close()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "AOGQnyg65H9d" + }, + "source": [ + "Така, дори и да получим грешка при четене (или каквато и да е друга операция в `try` блока), файла ще бъде затворен." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "qvu90-kN5oCy" + }, + "source": [ + "От тук можем да стигнем до заключението - при работата с ресурси, имаме процес по отваряне и процес по затваряне (независимо от грешки). Python ни предлага и друга синтактична конструкция за работа с такива структури - `with`." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "GpkO14Bw6Tue" + }, + "source": [ + "`with` ни позволява отварянето на нов контекст. В него може да отворим ресурс, а след излизането от блока, този ресурс се затваря автоматично. \n", + "\n", + "Общият вид на `with` е следния:\n", + "```python\n", + "with as :\n", + " f(variable)\n", + "```\n", + "\n", + "Ако искаме да отворим файл и да прочетем нещо с `with`, кода би изглеждал по следния начин:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "NXfepH8q7aLH", + "outputId": "44bea744-9a30-4e8c-f33a-b406f0211c61" + }, + "outputs": [], + "source": [ + "import os\n", + "with open(os.path.join('files', 'lorem_ipsum.txt')) as fp:\n", + " print(fp.read())\n", + "\n", + "print(f'Is the file closed ? {fp.closed}')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "EGihrAlq739A" + }, + "source": [ + "Дори и при грешка, файлът ще бъде затворен. Ако искаме обаче да хванем грешката, все пак трябва да изпозлваме `try/catch`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Gg93e1tN8Yeo", + "outputId": "48fb19db-f19d-42d0-fc1a-e11505055301" + }, + "outputs": [], + "source": [ + "import os\n", + "\n", + "try:\n", + " with open(os.path.join('files', 'lorem_ipsum.txt')) as fp:\n", + " print(fp.read())\n", + "except OSError as err:\n", + " print(err)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "8r0zhhbi9Pl8" + }, + "source": [ + "Начинът по който `with` работи е, че извиква два специални магически метода - `__enter__` и `__exit__`, който се изпълняват при влизане и излизане съответно от контекст мениджъра. \n", + "\n", + "По-конкретно, `__enter__` метода се изивиква в `as` частта на `with`. В него връщаме обекта, който ще бъде присвоен на променливата след `as`.\n", + "\n", + "\n", + "Файловите обекти имплементират `__exit__` метода, където затварят отворения файл." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "mItDZ8Bb-jV2" + }, + "source": [ + "Ако искаме и нашите класове да работят с контекстните мениджъри, трябва да имплементираме `__enter__` и `__exit__` методите. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "d6PErO_q-vza" + }, + "outputs": [], + "source": [ + "class ContextableClass:\n", + " def __init__(self, some_var=42):\n", + " self.some_var = some_var\n", + " \n", + " def __enter__(self):\n", + " print('Entering the context manager')\n", + " return self\n", + " \n", + " def __exit__(self, exc_type, exc_value, traceback):\n", + " print('Exiting the context manager')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "QwMZX5jF_2AX", + "outputId": "2cedd113-6b2a-4a64-9ea3-54b7e66f7aef" + }, + "outputs": [], + "source": [ + "with ContextableClass(5) as instance:\n", + " print(instance.some_var)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "BsHLVveUPgaD" + }, + "source": [ + "## `tell` и `seek`" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "CGJR7E48yy6F" + }, + "source": [ + "Писането и четенето от файлове всъщност се осъществява символ по символ. Така например за да запишем 'hello', трябва първо да запишем `h`, после `e` и т.н. \n", + "\n", + "Както при писането с клавиатура имаме позиция върху която пишем, така и при работата с файлове имаме позиция, на която четем или пишем.\n", + "\n", + "Python ни позволява да вземем текущата позиция за четене/писане, както и да преместим тази позиция на друго място. " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "CenG5LURMvZI" + }, + "source": [ + "Можем да вземем позиция на курсора във файла с помощта на метода `tell`. Той ще ни върне като резултат цяло число, символизиращо броя символи след началото на файла." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "n8djitSyM7Kh", + "outputId": "c9809392-5136-46db-b136-c76a5d4a8667" + }, + "outputs": [], + "source": [ + "import os\n", + "\n", + "file_path = os.path.join('files', 'tell_exampe.txt')\n", + "\n", + "# First lets create the file\n", + "with open(file_path, 'w') as fp:\n", + " fp.write('abc\\n')\n", + " fp.write('def\\n')\n", + " fp.write('ghi\\n')\n", + "\n", + "# Now lets read \n", + "with open(file_path) as fp:\n", + " print(f'Current position = {fp.tell()}')\n", + " print(f'Reading a line... {fp.readline()}')\n", + "\n", + " print(f'Current position = {fp.tell()}')\n", + " print(f'Reading another line... {fp.readline()}')\n", + "\n", + " print(f'Current position = {fp.tell()}')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "iGIdJZIGOIUL" + }, + "source": [ + "Можем да зададем позиция на курсора във файла с помощта на метода `seek`. Той приема два аргумента - какво отместване (`offset`) да направим и от къде (`whence`).\n", + "\n", + "`whence` приема три стойност:\n", + "- 0 (или `os.SEEK_SET`) - означава спрямо началото на файла\n", + "- 1 (или `os.SEEK_CUR`) - означава спрямо текущата позиция\n", + "- 2 (или `os.SEEK_END`) - означава спрямо края на файла\n", + "\n", + "При работа с текстови файлове, отместването е само спрямо началото на файла. Можем единствено да преместим курсора до края на файла с `seek(0, 2)`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "T3PRQ_lfOjK2", + "outputId": "96db36eb-4703-4be3-e22a-d178eea27e14" + }, + "outputs": [], + "source": [ + "import os\n", + "\n", + "file_path = os.path.join('files', 'seek_exampe.txt')\n", + "\n", + "# First lets create the file\n", + "with open(file_path, 'w') as fp:\n", + " fp.write('abcdefghijk')\n", + "\n", + "with open(file_path, 'r+') as fp:\n", + " fp.seek(2)\n", + " fp.write('!')\n", + "\n", + " fp.seek(0, 2)\n", + " fp.write('@')\n", + "\n", + "with open(file_path) as fp:\n", + " print(fp.read())" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "tjL0TPsTPn0a" + }, + "source": [ + "Бележка: Ако бяхте отворили файла за писане с режим `a`, щяхме да можем да пишем само в края. Режим `r+` ни дава права за четене и пренаписване. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## `pathlib`\n", + "\n", + "`pathlib` е вграден в езика модул, който предоставя обектно-ориентиран интерфейс за работа с пътища, чрез абстракция над всички гореспоменати функции." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Основния клас, който използваме от `pathlib` е `Path`. Можем да си го импортнем по следния начин:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`Path` предефинира оператор `/` за инуитивна конкатенация на пътища (алтернатива на `os.path.join`):" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "WindowsPath('data_folder/raw_datasets/some_dataset_dir/data')" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dataset_dir_path = Path(\"data_folder\") / \"raw_datasets\" / \"some_dataset_dir\"\n", + "meta_file_path = dataset_dir_path / \"meta.csv\"\n", + "data_dir_path = dataset_dir_path / \"data\"\n", + "\n", + "data_dir_path" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`Path`, както и `os.path.join`, автоматично проверяват дали пътят трябва да е Windows или Unix-съвместим. \n", + "\n", + "Details for nerds: В случая на `Path`, който е базов клас, при създаване на обект той автоматично преценява кой от двата subclass-a всъщност да създаде - `WindowsPath` или `PosixPath` (това е възможно чрез предефиниране на дъндъра `__new__`)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`Path` има доста полезни атрибути и методи:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "example_dir = Path('files')\n", + "example_file = example_dir / \"1.txt\"" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "WindowsPath('files')" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "example_file.parent" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "WindowsPath('.')" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "example_file.parent.parent" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'1.txt'" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "example_file.name" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'1'" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "example_file.stem" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'.txt'" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "example_file.suffix" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "example_file.is_file()" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. \\nUt enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. \\nDuis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. \\nExcepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum\\n'" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "example_file.read_text()" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "b'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. \\r\\nUt enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. \\r\\nDuis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. \\r\\nExcepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum\\r\\n'" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "example_file.read_bytes()" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": {}, + "outputs": [], + "source": [ + "new_file = example_dir / \"new_file.txt\"\n", + "new_file.touch() # creates an empty file" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "11" + ] + }, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "example_file.write_text('Hello world') # returns the number of bytes written" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": {}, + "outputs": [], + "source": [ + "renamed_file = new_file.rename(\"here.txt\") # can move as well" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": {}, + "outputs": [], + "source": [ + "renamed_file.unlink()" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "example_dir.is_dir()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[WindowsPath('files/1.txt')]" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(example_dir.iterdir()) # iterates over all files inside" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[WindowsPath('files/1.txt')]" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(example_dir.glob('*.txt')) # iterates over all files inside that end with .txt\n", + "\n", + "# globs can have special syntax for powerful filtering: https://en.wikipedia.org/wiki/Glob_(programming)" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "WindowsPath('C:/Users/Owner/Documents/PythonCourse2024/08 - Files/files/1.txt')" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "example_file.resolve() # relative path -> absolute path" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [], + "source": [ + "# example_file.owner() # not available on Windows" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": {}, + "outputs": [], + "source": [ + "new_dir = example_dir / \"new_dir\"\n", + "new_dir.mkdir()" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": {}, + "outputs": [], + "source": [ + "new_dir.rmdir()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Ако ви трябва някой `Path` обект като низ, може да използвате `str`:" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'files\\\\1.txt'" + ] + }, + "execution_count": 48, + "metadata": {}, + "output_type": "execute_result" } + ], + "source": [ + "str(example_file)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "YfLvF6IvlDpa" + }, + "source": [ + "## Примери" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "GQFz51abh-L-" + }, + "source": [ + "### Пример 1" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "VjoN0y7etjP4" + }, + "source": [ + "Напишете функция `split_path`, която приема път (като низ), и го разделя на съставните му части. Използвайте `os.path.split`." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "L0gvQsq5txfw" + }, + "source": [ + "#### Решение на пример 1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "0Ys1jhNfty_0" + }, + "outputs": [], + "source": [ + "import os\n", + "from typing import List\n", + "\n", + "def split_path(file_path: str) -> List[str]:\n", + " head, tail = os.path.split(file_path)\n", + "\n", + " result = [tail]\n", + " while head != '' and head != file_path:\n", + " file_path = head\n", + " head, tail = os.path.split(file_path)\n", + " result.append(tail)\n", + "\n", + " result.reverse()\n", + " return result" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "RhdgDKkna_mh", + "outputId": "f9447561-7d4e-4eb2-c1f3-39dbb4bbef0b" + }, + "outputs": [], + "source": [ + "print(split_path('/foo/bar/baz'))\n", + "print(split_path('/foo/bar/baz/'))\n", + "print(split_path('/foo'))\n", + "print(split_path('foo'))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ije_Z0fEXgKM" + }, + "source": [ + "### Пример 2" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "3d7BwPi4Xhtb" + }, + "source": [ + "Даден е клас `Person`, който съдържа информация за човек - неговите имена, рожденна дата, възраст и работа." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "3lCnmwaTXwbe" + }, + "outputs": [], + "source": [ + "class Person:\n", + " def __init__(self, first_name: str='', last_name: str='', birthdate: str='', age: int=0, job: str=''):\n", + " self.__first_name = first_name\n", + " self.__last_name = last_name\n", + " self.__birthdate = birthdate\n", + " self.__age = age\n", + " self.__job = job\n", + "\n", + " @property\n", + " def first_name(self) -> str:\n", + " return self.__first_name\n", + " \n", + " @property\n", + " def last_name(self) -> str:\n", + " return self.__last_name\n", + " \n", + " @property\n", + " def birthdate(self) -> str:\n", + " return self.__birthdate\n", + "\n", + " @property\n", + " def age(self) -> str:\n", + " return self.__age\n", + "\n", + " @property\n", + " def job(self) -> str:\n", + " return self.__job" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "GaGWV_nZYR2-" + }, + "source": [ + "Напишете клас `PersonSerializer`, съдържащ следните методи:\n", + "- Метод, който приема обект от тип `Person` и път. Методът записва във файл информацията за човека\n", + "- Метод, който приема път и връща обект от тип `Person`, който е създаден на база данните от файла\n", + "- Метод, който приема списък от `Person` и път. Методът записва във файл информация за хората\n", + "- Метод, който приема път и връща списък от `Person`, които са създадени на база на информацията във файла\n", + "\n", + "При грешка при писането или четенето класът да хвърля `ValueError`. \n", + "\n", + "Може да приемем, че информацията за един човек ще бъде записана на един ред. " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "TTMqcvL5ZbYj" + }, + "source": [ + "#### Решение на пример 2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "fQBxLoESZeQV" + }, + "outputs": [], + "source": [ + "from typing import List\n", + "\n", + "class Person:\n", + " def __init__(self, first_name: str='', last_name: str='', birthdate: str='', age: int=0, job: str=''):\n", + " self.__first_name = first_name\n", + " self.__last_name = last_name\n", + " self.__birthdate = birthdate\n", + " self.__age = age\n", + " self.__job = job\n", + "\n", + " @property\n", + " def first_name(self) -> str:\n", + " return self.__first_name\n", + " \n", + " @property\n", + " def last_name(self) -> str:\n", + " return self.__last_name\n", + " \n", + " @property\n", + " def birthdate(self) -> str:\n", + " return self.__birthdate\n", + "\n", + " @property\n", + " def age(self) -> str:\n", + " return self.__age\n", + "\n", + " @property\n", + " def job(self) -> str:\n", + " return self.__job\n", + "\n", + " # Adding a __str__ magic method helps us with printing the info\n", + " def __str__(self) -> str:\n", + " return f'{self.first_name}, {self.last_name}, {self.birthdate}, {self.age}, {self.job}'\n", + " \n", + " @staticmethod\n", + " def from_string(person_info: str):\n", + " first_name, last_name, birthdate, age, job = person_info.strip().split(', ')\n", + " return Person(first_name, last_name, birthdate, age, job)\n", + " \n", + "class PersonSerializer:\n", + " @staticmethod\n", + " def save_person(person: Person, filepath: str, mode: str='w'):\n", + " with open(filepath, mode) as fp:\n", + " fp.write(str(person) + '\\n')\n", + " \n", + " @staticmethod\n", + " def create_person_from_file(filepath: str) -> Person:\n", + " if not os.path.exists(filepath):\n", + " raise ValueError(f'File not found at {filepath}')\n", + " \n", + " with open(filepath) as fp:\n", + " person_info = fp.read()\n", + " return Person.from_string(person_info)\n", + "\n", + " @staticmethod\n", + " def save_people(people: List[Person], filepath: str):\n", + " for person in people:\n", + " PersonSerializer.save_person(person, filepath, mode='a')\n", + " \n", + " @staticmethod\n", + " def create_people_from_file(filepath: str) -> List[Person]:\n", + " if not os.path.exists(filepath):\n", + " raise ValueError(f'File not found at {filepath}')\n", + " \n", + " with open(filepath) as fp:\n", + " return [Person.from_string(person_info) for person_info in fp]\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "VGnQEoPnbcra", + "outputId": "e7a62425-76ec-4a98-d385-42db374f7494" + }, + "outputs": [], + "source": [ + "me = Person('Lyubo', 'Karev', '20-09-1998', 24, 'Developer')\n", + "my_filepath = os.path.join('files', 'lyubo.txt')\n", + "\n", + "PersonSerializer.save_person(me, my_filepath)\n", + "me_again = PersonSerializer.create_person_from_file(my_filepath) # Hello me, meet the real me\n", + "\n", + "print(me)\n", + "print(me_again)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "aju9NHCLcfBQ", + "outputId": "694cc2d1-f2af-44b9-d7fd-4c6e0913cd7a" + }, + "outputs": [], + "source": [ + "bunch_of_people = [\n", + " Person('Lyubo', 'Karev', '20-09-1998', 24, 'Developer'),\n", + " Person('Alex', 'Ignatov', '22-10-1998', 24, 'Developer'),\n", + " Person('Ivan', 'Luchev', '28-04-1998', 24, 'Student')\n", + "]\n", + "\n", + "our_filepath = os.path.join('files', 'us.txt')\n", + "\n", + "PersonSerializer.save_people(bunch_of_people, our_filepath)\n", + "us_again = PersonSerializer.create_people_from_file(our_filepath)\n", + "\n", + "print('Bunch of people:')\n", + "for person in bunch_of_people: \n", + " print(person)\n", + "\n", + "print('Us again:')\n", + "for person in us_again: \n", + " print(person)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Пример 3" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Както споменахме по-горе, `writelines` не добавя автоматично нови редове след всеки подаден елемент от списъка.\n", + "\n", + "Напишете функция `better_writelines`, която поправя тази грешка." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Решение на Пример 3" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "def better_writelines(lines: list[str], filepath: str):\n", + " with open(filepath, 'w') as fp:\n", + " fp.writelines([line + '\\n' for line in lines])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Пример 4" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`os.rmdir` изтрива директория, само ако е празна. \n", + "\n", + "Напишете функция `rm_rf`, която по подадена директория, изтрива съдържанието ѝ, както и самата директория." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Решение на Пример 4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def rm_rf(filepath: str):\n", + " if os.path.isdir(filepath):\n", + " for file in os.listdir(filepath):\n", + " rm_rf(os.path.join(filepath, file))\n", + " os.rmdir(filepath)\n", + " else:\n", + " os.remove(filepath)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Пример 5" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Напишете програма за управление на музикални файлове. \n", + "\n", + "Програмата трябва да поддържа следните функционалности:\n", + "- Задаване на работна директория (директория, в която са ни музикалните файлове)\n", + "- Извеждане на списък с всички песни (приемете, че всеки файл ще е с име във формат `artist-song_name.mp3`\n", + "- Търсене на песен по изпълнител (връща точно съвпадение)\n", + "- Търсене на псене по име на песен (връща точно съвпадение)\n", + "\n", + "Потребителския интефейс може да бъде следния:\n", + "- Потребителя има достъп до 5 команди (`set`, `list`, `find artist`, `find song` и `exit`)\n", + "- Потребителя може да въвежда (и изпълнява) команди, до въвеждане на `exit`, при което програмата спира работа.\n", + "(Ако имате други идеи за интерфейса, също ще бъдат приети)\n", + "\n", + "Примерни песни може да свалите от [тук](https://drive.google.com/file/d/1lqRxlPHd0THds_WU6l5DsRie0-yoCXFc/view?usp=sharing)" + ] + } + ], + "metadata": { + "colab": { + "authorship_tag": "ABX9TyN0PNFYqG1/FYzAoG4SAI8S", + "collapsed_sections": [], + "include_colab_link": true, + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" }, - "nbformat": 4, - "nbformat_minor": 0 + "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.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 0 }