{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "intro",
   "metadata": {},
   "source": [
    "# Writing files\n",
    "\n",
    "Writing files is essential for saving data, creating output, and building applications that persist information. In this tutorial, you will learn how to create new files, overwrite existing ones, and append content.\n",
    "\n",
    "**Time commitment:** 15–20 minutes\n",
    "\n",
    "**Prerequisites:**\n",
    "\n",
    "- Basic Python knowledge (variables, strings, lists)\n",
    "- Completion of [Reading files](https://agilearn.co.uk/guides/file-handling/learn/01-reading-files)\n",
    "\n",
    "## Learning objectives\n",
    "\n",
    "By the end of this tutorial, you will be able to:\n",
    "\n",
    "- Write text to files using `open()` with write mode\n",
    "- Append content to existing files\n",
    "- Use exclusive creation mode to prevent overwriting\n",
    "- Write multiple lines to a file\n",
    "- Use `pathlib.Path.write_text()` for simple write operations"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "write-heading",
   "metadata": {},
   "source": [
    "## Writing to a new file\n",
    "\n",
    "To write to a file, open it with write mode `\"w\"`. If the file does not exist, Python creates it. If it already exists, the contents are replaced."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "write-basic",
   "metadata": {},
   "outputs": [],
   "source": [
    "from pathlib import Path\n",
    "\n",
    "with open(\"output.txt\", \"w\", encoding=\"utf-8\") as f:\n",
    "    f.write(\"Hello, world!\\n\")\n",
    "    f.write(\"This is the second line.\\n\")\n",
    "\n",
    "print(Path(\"output.txt\").read_text(encoding=\"utf-8\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "overwrite-warning",
   "metadata": {},
   "source": [
    "!!! warning\n",
    "\n",
    "    Be careful with write mode `\"w\"` -- it will **overwrite** the entire file if it already exists. This is a common source of data loss."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "overwrite-heading",
   "metadata": {},
   "source": [
    "## Overwriting versus appending\n",
    "\n",
    "If you open a file in write mode `\"w\"` that already contains data, the existing content is completely replaced."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "overwrite-demo",
   "metadata": {},
   "outputs": [],
   "source": [
    "from pathlib import Path\n",
    "\n",
    "with open(\"output.txt\", \"w\", encoding=\"utf-8\") as f:\n",
    "    f.write(\"This replaces everything.\\n\")\n",
    "\n",
    "print(Path(\"output.txt\").read_text(encoding=\"utf-8\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "append-heading",
   "metadata": {},
   "source": [
    "## Appending to a file\n",
    "\n",
    "To add content to the end of an existing file without removing what is already there, use append mode `\"a\"`. If the file does not exist, Python creates it."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "append-demo",
   "metadata": {},
   "outputs": [],
   "source": [
    "from pathlib import Path\n",
    "\n",
    "with open(\"output.txt\", \"a\", encoding=\"utf-8\") as f:\n",
    "    f.write(\"This line is appended.\\n\")\n",
    "    f.write(\"And so is this one.\\n\")\n",
    "\n",
    "print(Path(\"output.txt\").read_text(encoding=\"utf-8\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "exclusive-heading",
   "metadata": {},
   "source": [
    "## Exclusive creation mode\n",
    "\n",
    "Exclusive creation mode `\"x\"` creates a new file but raises a `FileExistsError` if the file already exists. This prevents accidental overwriting."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "exclusive-demo",
   "metadata": {},
   "outputs": [],
   "source": [
    "from pathlib import Path\n",
    "\n",
    "# Remove the file first so we can demonstrate exclusive creation\n",
    "Path(\"safe-output.txt\").unlink(missing_ok=True)\n",
    "\n",
    "with open(\"safe-output.txt\", \"x\", encoding=\"utf-8\") as f:\n",
    "    f.write(\"Created safely.\\n\")\n",
    "\n",
    "print(Path(\"safe-output.txt\").read_text(encoding=\"utf-8\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "exclusive-error",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Trying to create the file again raises an error\n",
    "try:\n",
    "    with open(\"safe-output.txt\", \"x\", encoding=\"utf-8\") as f:\n",
    "        f.write(\"This will not work.\\n\")\n",
    "except FileExistsError:\n",
    "    print(\"FileExistsError: the file already exists.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "writelines-heading",
   "metadata": {},
   "source": [
    "## Writing multiple lines\n",
    "\n",
    "The `writelines()` method writes a list of strings to a file. Note that it does **not** add newline characters automatically &ndash; you must include them in each string."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "writelines-demo",
   "metadata": {},
   "outputs": [],
   "source": [
    "from pathlib import Path\n",
    "\n",
    "lines = [\"First line\\n\", \"Second line\\n\", \"Third line\\n\"]\n",
    "\n",
    "with open(\"multi.txt\", \"w\", encoding=\"utf-8\") as f:\n",
    "    f.writelines(lines)\n",
    "\n",
    "print(Path(\"multi.txt\").read_text(encoding=\"utf-8\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "print-heading",
   "metadata": {},
   "source": [
    "## Writing with `print()`\n",
    "\n",
    "You can use the `print()` function with the `file` parameter to write to a file. This is convenient because `print()` adds a newline automatically."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "print-demo",
   "metadata": {},
   "outputs": [],
   "source": [
    "from pathlib import Path\n",
    "\n",
    "with open(\"printed.txt\", \"w\", encoding=\"utf-8\") as f:\n",
    "    print(\"Line one\", file=f)\n",
    "    print(\"Line two\", file=f)\n",
    "    print(\"Line three\", file=f)\n",
    "\n",
    "print(Path(\"printed.txt\").read_text(encoding=\"utf-8\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "pathlib-heading",
   "metadata": {},
   "source": [
    "## Using `pathlib.Path.write_text()`\n",
    "\n",
    "`Path.write_text()` is the simplest way to write text to a file. It opens the file, writes the content, and closes it &ndash; all in one step. Note that it always overwrites the existing content."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "pathlib-demo",
   "metadata": {},
   "outputs": [],
   "source": [
    "from pathlib import Path\n",
    "\n",
    "Path(\"simple.txt\").write_text(\"Simple content.\\n\", encoding=\"utf-8\")\n",
    "print(Path(\"simple.txt\").read_text(encoding=\"utf-8\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "pathlib-explain",
   "metadata": {},
   "source": [
    "For appending content, use `Path.open()` with append mode."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "practical-heading",
   "metadata": {},
   "source": [
    "## Writing data from a list\n",
    "\n",
    "A common task is writing data from a list, with each item on a separate line."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "practical-demo",
   "metadata": {},
   "outputs": [],
   "source": [
    "from pathlib import Path\n",
    "\n",
    "names = [\"Alice\", \"Bob\", \"Charlie\"]\n",
    "\n",
    "with Path(\"names.txt\").open(\"w\", encoding=\"utf-8\") as f:\n",
    "    for name in names:\n",
    "        f.write(name + \"\\n\")\n",
    "\n",
    "print(Path(\"names.txt\").read_text(encoding=\"utf-8\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "exercises-heading",
   "metadata": {},
   "source": [
    "## Exercises\n",
    "\n",
    "Try these exercises to practise what you have learned.\n",
    "\n",
    "**Exercise 1:** Create a file called `shopping.txt` with a list of five items, one per line. Then append two more items to the file.\n",
    "\n",
    "**Exercise 2:** Write a function called `save_lines` that takes a file path and a list of strings, and writes each string to the file on a separate line. Use type hints and `pathlib.Path`.\n",
    "\n",
    "**Exercise 3:** Write a function called `append_timestamp` that appends the current date and time to a file. Use `datetime.datetime.now()`."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "solutions-heading",
   "metadata": {},
   "source": [
    "### Solutions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "solution-1",
   "metadata": {},
   "outputs": [],
   "source": [
    "from pathlib import Path\n",
    "\n",
    "items = [\"Bread\", \"Milk\", \"Eggs\", \"Butter\", \"Cheese\"]\n",
    "with open(\"shopping.txt\", \"w\", encoding=\"utf-8\") as f:\n",
    "    for item in items:\n",
    "        f.write(item + \"\\n\")\n",
    "\n",
    "with open(\"shopping.txt\", \"a\", encoding=\"utf-8\") as f:\n",
    "    f.write(\"Apples\\n\")\n",
    "    f.write(\"Bananas\\n\")\n",
    "\n",
    "print(Path(\"shopping.txt\").read_text(encoding=\"utf-8\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "solution-2",
   "metadata": {},
   "outputs": [],
   "source": [
    "from pathlib import Path\n",
    "\n",
    "\n",
    "def save_lines(filepath: str | Path, lines: list[str]) -> None:\n",
    "    \"\"\"Write a list of strings to a file, each on a separate line.\n",
    "\n",
    "    Args:\n",
    "        filepath: The path to the file to write.\n",
    "        lines: A list of strings to write as lines.\n",
    "    \"\"\"\n",
    "    path = Path(filepath)\n",
    "    with path.open(\"w\", encoding=\"utf-8\") as f:\n",
    "        for line in lines:\n",
    "            f.write(line + \"\\n\")\n",
    "\n",
    "\n",
    "save_lines(\"colours.txt\", [\"Red\", \"Green\", \"Blue\"])\n",
    "print(Path(\"colours.txt\").read_text(encoding=\"utf-8\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "solution-3",
   "metadata": {},
   "outputs": [],
   "source": [
    "from datetime import datetime\n",
    "from pathlib import Path\n",
    "\n",
    "\n",
    "def append_timestamp(filepath: str | Path) -> None:\n",
    "    \"\"\"Append the current date and time to a file.\n",
    "\n",
    "    Args:\n",
    "        filepath: The path to the file to append to.\n",
    "    \"\"\"\n",
    "    path = Path(filepath)\n",
    "    timestamp = datetime.now().strftime(\"%d/%m/%Y %H:%M:%S\")\n",
    "    with path.open(\"a\", encoding=\"utf-8\") as f:\n",
    "        f.write(timestamp + \"\\n\")\n",
    "\n",
    "\n",
    "append_timestamp(\"timestamps.txt\")\n",
    "append_timestamp(\"timestamps.txt\")\n",
    "print(Path(\"timestamps.txt\").read_text(encoding=\"utf-8\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cleanup",
   "metadata": {},
   "outputs": [],
   "source": [
    "from pathlib import Path\n",
    "\n",
    "for filename in [\"output.txt\", \"safe-output.txt\", \"multi.txt\", \"printed.txt\",\n",
    "                 \"simple.txt\", \"names.txt\", \"shopping.txt\", \"colours.txt\",\n",
    "                 \"timestamps.txt\"]:\n",
    "    Path(filename).unlink(missing_ok=True)\n",
    "\n",
    "print(\"Temporary files removed.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "summary",
   "metadata": {},
   "source": [
    "## Summary\n",
    "\n",
    "In this tutorial, you learned how to write and append to text files. Here are the key takeaways:\n",
    "\n",
    "- `\"w\"` mode creates or overwrites files\n",
    "- `\"a\"` mode appends to existing files (or creates new ones)\n",
    "- `\"x\"` mode creates files safely, raising an error if the file already exists\n",
    "- `writelines()` writes a list of strings but does not add newlines automatically\n",
    "- `print()` with the `file` parameter is convenient for formatted output\n",
    "- `Path.write_text()` is the simplest approach for writing entire files\n",
    "- Always specify `encoding=\"utf-8\"` for consistent behaviour\n",
    "\n",
    "In the [next tutorial](https://agilearn.co.uk/guides/file-handling/learn/03-working-with-paths), you will learn how to work with file paths using `pathlib`."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python",
   "version": "3.12.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}