{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "73703282",
   "metadata": {},
   "source": [
    "## Lecture 17, 14 October 2025"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0c0384c6-ef60-4ce2-a1e6-05a0af37c219",
   "metadata": {},
   "source": [
    "- String formatting -- positional arguments"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "db4ff1ce-f4ab-44c4-abbb-66f048be7edf",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'First one is 22, second one is 7'"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = 7\n",
    "y = 22\n",
    "message = \"First one is {1}, second one is {0}\"\n",
    "message.format(x,y)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "851bb7d6-0709-49c5-97d0-c68735cea9cf",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'First one is 22, second one is 7. To repeat, first one is 22.'"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = 7\n",
    "y = 22\n",
    "message1 = \"First one is {1}, second one is {0}. To repeat, first one is {1}.\"\n",
    "message1.format(x,y)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a5d3ea09-a024-44b7-8cd8-a06ec77ea7e7",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9e9e387d-9c2b-44b3-8c32-21a307db12de",
   "metadata": {},
   "source": [
    "- Raising an exception in `List()`\n",
    "- Inserting a negative value raises `ValueError`\n",
    "- Use string formatting to add negative value to error message"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "3f7a6da5-1fdd-4553-9d7d-5818d77aef25",
   "metadata": {},
   "outputs": [],
   "source": [
    "class List:\n",
    "    def __init__(self,initlist = []):\n",
    "        self.value = None\n",
    "        self.next = None\n",
    "        for x in initlist:\n",
    "            self.append(x)\n",
    "        return\n",
    "\n",
    "    def isempty(self):\n",
    "        return(self.value == None)\n",
    "    \n",
    "    def appendi(self,v):   # append, iterative\n",
    "        if v < 0:\n",
    "            raise ValueError(\"Negative input:{0}\".format(v))\n",
    "        if self.isempty():\n",
    "            self.value = v\n",
    "            return\n",
    "        \n",
    "        temp = self\n",
    "        while temp.next != None:\n",
    "            temp = temp.next\n",
    "\n",
    "        temp.next = List()\n",
    "        temp.next.value = v \n",
    "        return\n",
    "\n",
    "    def appendr(self,v):   # append, recursive\n",
    "        if v < 0:\n",
    "            raise ValueError(\"Negative input:{0}\".format(v))\n",
    "        if self.isempty():\n",
    "            self.value = v\n",
    "        elif self.next == None:\n",
    "            self.next = List([v])\n",
    "        else:\n",
    "            self.next.appendr(v)\n",
    "        return\n",
    "\n",
    "    def append(self,v):\n",
    "        self.appendr(v)\n",
    "        return\n",
    "\n",
    "    def insert(self,v):\n",
    "        if v < 0:\n",
    "            raise ValueError(\"Negative input:{0}\".format(v))\n",
    "        if self.isempty():\n",
    "            self.value = v\n",
    "            return\n",
    "\n",
    "        newnode = List()\n",
    "        newnode.value = v\n",
    "        \n",
    "        # Exchange values in self and newnode\n",
    "        (self.value, newnode.value) = (newnode.value, self.value)\n",
    "\n",
    "        # Switch links\n",
    "        (self.next, newnode.next) = (newnode, self.next)\n",
    "\n",
    "        return\n",
    "\n",
    "    def delete(self,v):   # delete, recursive\n",
    "        if self.isempty():\n",
    "            return\n",
    "\n",
    "        if self.value == v:\n",
    "            self.value = None\n",
    "            if self.next != None:\n",
    "                self.value = self.next.value\n",
    "                self.next = self.next.next\n",
    "            return\n",
    "        else:\n",
    "            if self.next != None:\n",
    "                self.next.delete(v)\n",
    "                if self.next.value == None:\n",
    "                    self.next = None\n",
    "        return\n",
    "    \n",
    "    def __str__(self):\n",
    "        # Iteratively create a Python list from linked list\n",
    "        # and convert that to a string\n",
    "        selflist = []\n",
    "        if self.isempty():\n",
    "            return(str(selflist))\n",
    "\n",
    "        temp = self\n",
    "        selflist.append(temp.value)\n",
    "        \n",
    "        while temp.next != None:\n",
    "          temp = temp.next\n",
    "          selflist.append(temp.value)\n",
    "\n",
    "        return(str(selflist))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "064e26ad-9096-48fe-a714-f5396d68a931",
   "metadata": {},
   "outputs": [
    {
     "ename": "ValueError",
     "evalue": "Negative input:-2",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mValueError\u001b[39m                                Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[4]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m l = \u001b[43mList\u001b[49m\u001b[43m(\u001b[49m\u001b[43m[\u001b[49m\u001b[32;43m1\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m-\u001b[49m\u001b[32;43m2\u001b[39;49m\u001b[43m,\u001b[49m\u001b[32;43m3\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m      2\u001b[39m \u001b[38;5;28mprint\u001b[39m(l)\n",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[3]\u001b[39m\u001b[32m, line 6\u001b[39m, in \u001b[36mList.__init__\u001b[39m\u001b[34m(self, initlist)\u001b[39m\n\u001b[32m      4\u001b[39m \u001b[38;5;28mself\u001b[39m.next = \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[32m      5\u001b[39m \u001b[38;5;28;01mfor\u001b[39;00m x \u001b[38;5;129;01min\u001b[39;00m initlist:\n\u001b[32m----> \u001b[39m\u001b[32m6\u001b[39m     \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mappend\u001b[49m\u001b[43m(\u001b[49m\u001b[43mx\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m      7\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m\n",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[3]\u001b[39m\u001b[32m, line 39\u001b[39m, in \u001b[36mList.append\u001b[39m\u001b[34m(self, v)\u001b[39m\n\u001b[32m     38\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mappend\u001b[39m(\u001b[38;5;28mself\u001b[39m,v):\n\u001b[32m---> \u001b[39m\u001b[32m39\u001b[39m     \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mappendr\u001b[49m\u001b[43m(\u001b[49m\u001b[43mv\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m     40\u001b[39m     \u001b[38;5;28;01mreturn\u001b[39;00m\n",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[3]\u001b[39m\u001b[32m, line 29\u001b[39m, in \u001b[36mList.appendr\u001b[39m\u001b[34m(self, v)\u001b[39m\n\u001b[32m     27\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mappendr\u001b[39m(\u001b[38;5;28mself\u001b[39m,v):   \u001b[38;5;66;03m# append, recursive\u001b[39;00m\n\u001b[32m     28\u001b[39m     \u001b[38;5;28;01mif\u001b[39;00m v < \u001b[32m0\u001b[39m:\n\u001b[32m---> \u001b[39m\u001b[32m29\u001b[39m         \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[33m\"\u001b[39m\u001b[33mNegative input:\u001b[39m\u001b[38;5;132;01m{0}\u001b[39;00m\u001b[33m\"\u001b[39m.format(v))\n\u001b[32m     30\u001b[39m     \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m.isempty():\n\u001b[32m     31\u001b[39m         \u001b[38;5;28mself\u001b[39m.value = v\n",
      "\u001b[31mValueError\u001b[39m: Negative input:-2"
     ]
    }
   ],
   "source": [
    "l = List([1,-2,3])\n",
    "print(l)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f4476d5f-f394-40dc-a8c3-222eea54f225",
   "metadata": {},
   "source": [
    "- String formatting with output specification"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "073cfee7-bbbe-4600-b01c-3dd44683e9f7",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Value   747.30'"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "\"Value {0:8.2f}\".format(747.3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "ccae5580-1697-46e6-b659-8d562d7aa518",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Value 99999999947.34         99999999947.3444061'"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "\"Value {0:6.2f} {0:27.7f}\".format(99999999947.3444,22)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "878d0f6f-095f-4094-8cf8-40d2addea29e",
   "metadata": {},
   "source": [
    "- Modifying behaviour of `print()`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "cdf9fba0-d448-4974-98ec-c6cb52a2bd1e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "5 7\n",
      "7\n"
     ]
    }
   ],
   "source": [
    "x = 5\n",
    "y = 7\n",
    "print(x,y)\n",
    "print(y)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "eececd0a-931d-4d02-8e81-5e201d9da492",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "5:7\n"
     ]
    }
   ],
   "source": [
    "x = 5\n",
    "y = 7\n",
    "print(x,y,sep=\":\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "56941186-e25e-45e4-80da-493e5e32d5fa",
   "metadata": {},
   "outputs": [],
   "source": [
    "l = list(range(20))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "10a1a43d-6f49-4335-9c2e-762a18ef17c8",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, Where are we?\n"
     ]
    }
   ],
   "source": [
    "for x in l:\n",
    "    print(x,end=\", \")\n",
    "print(\"Where are we?\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "cba6f5e8-e775-4c1b-afea-84053fe8a910",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, \n",
      "Where are we?\n"
     ]
    }
   ],
   "source": [
    "for x in l:\n",
    "    print(x,end=\", \")\n",
    "print()\n",
    "print(\"Where are we?\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2373c175",
   "metadata": {},
   "source": [
    "### Working with files"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e1ff2150",
   "metadata": {},
   "source": [
    "#### Reading files"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5c894c91",
   "metadata": {},
   "source": [
    "- Open a file in mode `\"r\"`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "d10ba375",
   "metadata": {},
   "outputs": [],
   "source": [
    "fh = open(\"oz.txt\",\"r\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d0b07cf3",
   "metadata": {},
   "source": [
    "- `read()` returns a string with the entire file\n",
    "- Note the `\\n` characters indicating end of line (\"new line\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "091e5a89",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'I met a traveller from an antique land\\nWho said: Two vast and trunkless legs of stone\\nStand in the desaet. Near them, on the sand,\\nHalf sunk, a shattered visage lies, whose frown,\\nAnd wrinkled lip, and sneer of cold command,\\nTell that its sculptor well those passions read\\nWhich yet survive, stamped on these lifeless things,\\nThe hand that mocked them and the heart that fed:\\nAnd on the pedestal these words appear:\\n\"My name is Ozymandias, King of Kings:\\nLook on my works, ye Mighty, and despair!\"\\nNo thing beside remains. Round the decay\\nOf that colossal wreck, boundless and bare\\nThe lone and level sands stretch far away. \\n'"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "contents = fh.read()\n",
    "contents"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4bbcc920",
   "metadata": {},
   "source": [
    "- After reading the file, we are at the end\n",
    "- End of file is indicated by `read()` returning the empty string"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "8f263551",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "''"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fh.read()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "458dda06-887f-4bc4-9581-57ed5e6e619a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "''"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fh.read()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b195f342",
   "metadata": {},
   "source": [
    "- To re-read the file, we have to close it and open it again"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "42e89464",
   "metadata": {},
   "outputs": [],
   "source": [
    "fh.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d165563b",
   "metadata": {},
   "source": [
    "- `readline()` reads one line\n",
    "- Unlike `input()`, the line that is read includes the terminating `\\n'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "5e8201d3",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'I met a traveller from an antique land\\n'"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fh = open(\"oz.txt\",\"r\")\n",
    "line1 = fh.readline()\n",
    "line1"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9565ded2",
   "metadata": {},
   "source": [
    "- `readlines()` returns the entire file as a list of lines\n",
    "- Each line is terminated by `\\n`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "4db098e5",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['I met a traveller from an antique land\\n',\n",
       " 'Who said: Two vast and trunkless legs of stone\\n',\n",
       " 'Stand in the desaet. Near them, on the sand,\\n',\n",
       " 'Half sunk, a shattered visage lies, whose frown,\\n',\n",
       " 'And wrinkled lip, and sneer of cold command,\\n',\n",
       " 'Tell that its sculptor well those passions read\\n',\n",
       " 'Which yet survive, stamped on these lifeless things,\\n',\n",
       " 'The hand that mocked them and the heart that fed:\\n',\n",
       " 'And on the pedestal these words appear:\\n',\n",
       " '\"My name is Ozymandias, King of Kings:\\n',\n",
       " 'Look on my works, ye Mighty, and despair!\"\\n',\n",
       " 'No thing beside remains. Round the decay\\n',\n",
       " 'Of that colossal wreck, boundless and bare\\n',\n",
       " 'The lone and level sands stretch far away. \\n']"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fh.close()\n",
    "fh = open(\"oz.txt\",\"r\")\n",
    "alllines = fh.readlines()\n",
    "alllines"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7e33c938",
   "metadata": {},
   "source": [
    "- `readlines()` returns the lines from the current position\n",
    "- In the following example, the list returned is from the second line onwards"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "63b77307",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['Who said: Two vast and trunkless legs of stone\\n',\n",
       " 'Stand in the desaet. Near them, on the sand,\\n',\n",
       " 'Half sunk, a shattered visage lies, whose frown,\\n',\n",
       " 'And wrinkled lip, and sneer of cold command,\\n',\n",
       " 'Tell that its sculptor well those passions read\\n',\n",
       " 'Which yet survive, stamped on these lifeless things,\\n',\n",
       " 'The hand that mocked them and the heart that fed:\\n',\n",
       " 'And on the pedestal these words appear:\\n',\n",
       " '\"My name is Ozymandias, King of Kings:\\n',\n",
       " 'Look on my works, ye Mighty, and despair!\"\\n',\n",
       " 'No thing beside remains. Round the decay\\n',\n",
       " 'Of that colossal wreck, boundless and bare\\n',\n",
       " 'The lone and level sands stretch far away. \\n']"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fh.close()\n",
    "fh = open(\"oz.txt\",\"r\")\n",
    "line1 = fh.readline()      # Read first line\n",
    "alllines = fh.readlines()  # From second line onwards\n",
    "alllines"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8cb72b87",
   "metadata": {},
   "source": [
    "#### Stripping white space"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c0a39180",
   "metadata": {},
   "source": [
    "- We can strip off leading and trailing \"white space\" (blank, tab, newline) from a string\n",
    "- `rstrip()` removes trailing whitespace\n",
    "- Note that strings are immutable, so the function returns a new string, leaving the original untouched"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "eecfeaf4",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'   abc    \\n'"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "teststr = \"   abc    \\n\"\n",
    "teststr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "14df3215",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'   abc'"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "teststr.rstrip()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "1d19792e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'   abc    \\n'"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "teststr"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b4972cac",
   "metadata": {},
   "source": [
    "- `lstrip()` removes leading whitespace"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "22bcae5f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'abc    \\n'"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "teststr.lstrip()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c6bfd2b5",
   "metadata": {},
   "source": [
    "- `strip()` removes whitespace at both ends"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "d28ffad5",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'abc'"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "teststr.strip()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f6011298",
   "metadata": {},
   "source": [
    "- Can use these to quickly remove `\\n` from lines that we read from a file\n",
    "- In the first loop below, there is a blank line between actual lines because of the `\\n` in the input line followed by the newline inserted by `print()`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "f6821ec7",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "I met a traveller from an antique land\n",
      "\n",
      "Who said: Two vast and trunkless legs of stone\n",
      "\n",
      "Stand in the desaet. Near them, on the sand,\n",
      "\n",
      "Half sunk, a shattered visage lies, whose frown,\n",
      "\n",
      "And wrinkled lip, and sneer of cold command,\n",
      "\n",
      "Tell that its sculptor well those passions read\n",
      "\n",
      "Which yet survive, stamped on these lifeless things,\n",
      "\n",
      "The hand that mocked them and the heart that fed:\n",
      "\n",
      "And on the pedestal these words appear:\n",
      "\n",
      "\"My name is Ozymandias, King of Kings:\n",
      "\n",
      "Look on my works, ye Mighty, and despair!\"\n",
      "\n",
      "No thing beside remains. Round the decay\n",
      "\n",
      "Of that colossal wreck, boundless and bare\n",
      "\n",
      "The lone and level sands stretch far away. \n",
      "\n"
     ]
    }
   ],
   "source": [
    "fh.close()\n",
    "fh = open(\"oz.txt\",\"r\")\n",
    "for l in fh.readlines():\n",
    "    print(l)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b64063b1",
   "metadata": {},
   "source": [
    "- If we strip each line before printing, the blank lines are eliminated"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "14e8793e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "I met a traveller from an antique land\n",
      "Who said: Two vast and trunkless legs of stone\n",
      "Stand in the desaet. Near them, on the sand,\n",
      "Half sunk, a shattered visage lies, whose frown,\n",
      "And wrinkled lip, and sneer of cold command,\n",
      "Tell that its sculptor well those passions read\n",
      "Which yet survive, stamped on these lifeless things,\n",
      "The hand that mocked them and the heart that fed:\n",
      "And on the pedestal these words appear:\n",
      "\"My name is Ozymandias, King of Kings:\n",
      "Look on my works, ye Mighty, and despair!\"\n",
      "No thing beside remains. Round the decay\n",
      "Of that colossal wreck, boundless and bare\n",
      "The lone and level sands stretch far away.\n"
     ]
    }
   ],
   "source": [
    "fh.close()\n",
    "fh = open(\"oz.txt\",\"r\")\n",
    "for l in fh.readlines():\n",
    "    print(l.rstrip())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "87438999",
   "metadata": {},
   "source": [
    "#### Writing files"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d9d57ac3",
   "metadata": {},
   "source": [
    "- Open a file in mode `\"w\"`\n",
    "- Opening a non-existent file for reading generates an error\n",
    "- Opening a non-existent file for writing creates a new file\n",
    "- If the file already exists, write will overwrite the contents"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "d4c84ad4",
   "metadata": {},
   "outputs": [
    {
     "ename": "FileNotFoundError",
     "evalue": "[Errno 2] No such file or directory: 'newfile.txt'",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mFileNotFoundError\u001b[39m                         Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[27]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m infile = \u001b[38;5;28;43mopen\u001b[39;49m\u001b[43m(\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mnewfile.txt\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mr\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~/python-venv/lib/python3.13/site-packages/IPython/core/interactiveshell.py:343\u001b[39m, in \u001b[36m_modified_open\u001b[39m\u001b[34m(file, *args, **kwargs)\u001b[39m\n\u001b[32m    336\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m file \u001b[38;5;129;01min\u001b[39;00m {\u001b[32m0\u001b[39m, \u001b[32m1\u001b[39m, \u001b[32m2\u001b[39m}:\n\u001b[32m    337\u001b[39m     \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\n\u001b[32m    338\u001b[39m         \u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mIPython won\u001b[39m\u001b[33m'\u001b[39m\u001b[33mt let you open fd=\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mfile\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m by default \u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m    339\u001b[39m         \u001b[33m\"\u001b[39m\u001b[33mas it is likely to crash IPython. If you know what you are doing, \u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m    340\u001b[39m         \u001b[33m\"\u001b[39m\u001b[33myou can use builtins\u001b[39m\u001b[33m'\u001b[39m\u001b[33m open.\u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m    341\u001b[39m     )\n\u001b[32m--> \u001b[39m\u001b[32m343\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mio_open\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfile\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n",
      "\u001b[31mFileNotFoundError\u001b[39m: [Errno 2] No such file or directory: 'newfile.txt'"
     ]
    }
   ],
   "source": [
    "infile = open(\"newfile.txt\",\"r\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "364356fa",
   "metadata": {},
   "outputs": [],
   "source": [
    "outfile = open(\"newfile.txt\",\"w\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "ca6379dc",
   "metadata": {},
   "outputs": [],
   "source": [
    "outfile.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8fa93f1d",
   "metadata": {},
   "source": [
    "- `fh.write(s)` writes the string `s` to the file associated with file handle `fh`\n",
    "- Need to add `\\n` ourselves to ensure the end of line\n",
    "- `fh.writelines(sl)` writes a list of strings `ls`\n",
    "- Again, we need to ensure `\\n` is present at the end of each string in the list\n",
    "    - The name of the function is misleading\n",
    "    - It is more accurate to call it `writestrings()` since we have to insert `\\n` manually"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e02c74e0",
   "metadata": {},
   "source": [
    "- The following loop copies the input to the output\n",
    "    - Each line that is read includes the `\\n`\n",
    "    - Hence each line that is written also has the corresponding `\\n`\n",
    "- However, after this loop, typically the output file will be empty\n",
    "- Need to close the file handle for the buffer to be \"flushed\" to the disk"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "8c198b1f",
   "metadata": {},
   "outputs": [],
   "source": [
    "infile = open(\"oz.txt\",\"r\")\n",
    "outfile = open(\"newfile.txt\",\"w\")\n",
    "for l in infile.readlines():\n",
    "    outfile.write(l)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "77715c0a-b989-49b9-8435-20d4d89246c0",
   "metadata": {},
   "outputs": [],
   "source": [
    "outfile.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6804e2a0",
   "metadata": {},
   "source": [
    "- Close all file handles when you are done with them"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "id": "38030353",
   "metadata": {},
   "outputs": [],
   "source": [
    "infile = open(\"oz.txt\",\"r\")\n",
    "outfile = open(\"newfile.txt\",\"w\")\n",
    "for l in infile.readlines():\n",
    "    outfile.write(l)\n",
    "infile.close()\n",
    "outfile.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1a539db0",
   "metadata": {},
   "source": [
    "- We can also read all the lines into a list using `readlines()` and write them out using `writelines()`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "id": "6f9f71ab",
   "metadata": {},
   "outputs": [],
   "source": [
    "infile = open(\"oz.txt\",\"r\")\n",
    "outfile = open(\"newfile.txt\",\"w\")\n",
    "contents = infile.readlines()\n",
    "outfile.writelines(contents)\n",
    "infile.close()\n",
    "outfile.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9e062fec",
   "metadata": {},
   "source": [
    "#### Appending output"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f1f9119d",
   "metadata": {},
   "source": [
    "- We can append our writes at the end of a file\n",
    "- Open the file with mode `\"a\"` instead of `\"w\"`\n",
    "- After the following, `newfile.txt` should have two copies of `oz.txt`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "id": "5687bedf",
   "metadata": {},
   "outputs": [],
   "source": [
    "infile = open(\"oz.txt\",\"r\")\n",
    "outfile = open(\"newfile.txt\",\"a\")\n",
    "contents = infile.readlines()\n",
    "outfile.writelines(contents)\n",
    "infile.close()\n",
    "outfile.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5e9a6b8b",
   "metadata": {},
   "source": [
    "- There are other functions we have not discussed\n",
    "- `fh.seek(n)` moves to position `n` in the file\n",
    "    - After reading a file, use `fh.seek(0)` to return to the start\n",
    "- `fh.read(n)` reads `n` characters from the current position"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "id": "079522be",
   "metadata": {},
   "outputs": [],
   "source": [
    "infile = open(\"oz.txt\",\"r\")\n",
    "c1 = infile.read()\n",
    "infile.seek(0)\n",
    "c2 = infile.read(20)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "id": "b1cbe124",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'I met a traveller from an antique land\\nWho said: Two vast and trunkless legs of stone\\nStand in the desaet. Near them, on the sand,\\nHalf sunk, a shattered visage lies, whose frown,\\nAnd wrinkled lip, and sneer of cold command,\\nTell that its sculptor well those passions read\\nWhich yet survive, stamped on these lifeless things,\\nThe hand that mocked them and the heart that fed:\\nAnd on the pedestal these words appear:\\n\"My name is Ozymandias, King of Kings:\\nLook on my works, ye Mighty, and despair!\"\\nNo thing beside remains. Round the decay\\nOf that colossal wreck, boundless and bare\\nThe lone and level sands stretch far away. \\n'"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "c1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "id": "b06b37ed",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'I met a traveller fr'"
      ]
     },
     "execution_count": 37,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "c2"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
