{"id":"reading-files-line-by-line","slug":"line-by-line","title":"Going through a file line by line","text":"`read()` gives you the whole file at once. More often a program handles a file **one line at a time**, and a file handle makes that easy: it works in a `for` loop much like a list of strings. Each round, the loop variable gets the next line of the file.\n\n```file planets.txt\nMercury\nVenus\nEarth\n```\n\n```python\nwith open(\"planets.txt\") as file:\n    for line in file:\n        print(line)\n```\n\n```output\nMercury\n\nVenus\n\nEarth\n```\n\nWhere do the empty lines come from? Each line of a file ends with a line break, the character `\\n`, and the line Python reads includes it: the first line is `\"Mercury\\n\"`. `print` then adds a line break of its own, so every line is followed by an empty one. (The very last line of a file may or may not end with `\\n`, depending on how the file was saved.)\n\n### Removing the line break with strip\n\nYou have met the string method `strip` in the module on strings and lists. It returns a copy of a string without the **whitespace** at its start and end, and whitespace means line breaks and tabs as well as spaces. Spaces in the middle stay where they are. `strip` has two relatives: **`rstrip`** removes whitespace only from the right end of the string, and **`lstrip`** only from the left end.\n\n```pycon\n>>> \"Venus\\n\".strip()\n'Venus'\n>>> \"   New York  \\n\".strip()\n'New York'\n>>> len(\"Venus\\n\")\n6\n>>> len(\"Venus\\n\".strip())\n5\n>>> \"  indented line\\n\".rstrip()\n'  indented line'\n>>> \"  indented line\\n\".lstrip()\n'indented line\\n'\n```\n\nLike every string method, `strip` doesn't change the string it's called on (strings can't be changed). It returns a new string, so store the result: `line = line.strip()`.\n\n### Why the line break matters\n\nAn empty line in the printout is the harmless case. Comparisons are worse: `\"Earth\\n\" == \"Earth\"` is `False`, so a program that looks for the line `Earth` without stripping the lines never finds it, and the length of every line is one too many. Make `strip` a habit whenever you read lines from a file.\n\nLines that hold numbers are converted with `int` or `float` as usual. This file has the steps someone walked on each of three days:\n\n```file steps.txt\n4200\n8100\n6300\n```\n\n```python\ntotal = 0\ndays = 0\nwith open(\"steps.txt\") as file:\n    for line in file:\n        steps = int(line.strip())\n        days += 1\n        total += steps\n        print(f\"Day {days}: {steps} steps\")\nprint(\"Average:\", total / days)\n```\n\n```output\nDay 1: 4200 steps\nDay 2: 8100 steps\nDay 3: 6300 steps\nAverage: 6200.0\n```\n\n`int` would forgive the line break here (it ignores whitespace around a number), but stripping first keeps the habit the same for every line. A file can also end with an empty line; after `strip` it is the empty string `\"\"`, which a program can skip with `if line == \"\": continue`.\n","task":"Fill in the blanks so the program greets each guest in the file `guests.txt`, one per line. With this file\n\n```text\nAda\nGrace\nLinus\n```\n\nthe program prints:\n\n```\nWelcome, Ada!\nWelcome, Grace!\nWelcome, Linus!\n```\n","kind":"fill","seed":"with open(\"guests.txt\") as file:\n    for line in [[file]]:\n        name = line.[[strip‖rstrip]]()\n        print(f\"Welcome, {name}!\")\n","solution":"with open(\"guests.txt\") as file:\n    for line in file:\n        name = line.strip()\n        print(f\"Welcome, {name}!\")\n","hints":["The `for` loop goes through the file handle, the variable named after `as`.","Each line ends with `\\n`, which would end up before the `!`. The method that removes it is `strip`."],"tests":[{"name":"Test 1","stdin":"","files":{"guests.txt":"Ada\nGrace\nLinus\n"},"expect":"Welcome, Ada!\nWelcome, Grace!\nWelcome, Linus!","hidden":false},{"name":"Test 2","stdin":"","files":{"guests.txt":"Margaret\nTim\n"},"expect":"Welcome, Margaret!\nWelcome, Tim!","hidden":true}],"require":[],"forbid":[],"more":[{"task":"What does this program print? Type each line it prints.\n","kind":"predict","seed":"word = \"  tea \\n\"\nprint(len(word))\nprint(len(word.strip()))\nprint(len(word.rstrip()))\nprint(word.strip() == \"tea\")\n","solution":"word = \"  tea \\n\"\nprint(len(word))\nprint(len(word.strip()))\nprint(len(word.rstrip()))\nprint(word.strip() == \"tea\")\n","hints":["`len` counts every character: the two spaces at the start, the three letters, the space after them and the line break.","`strip` removes the whitespace at both ends, leaving `tea`. `rstrip` removes it only at the right end, so the two spaces at the start stay."],"tests":[{"name":"Output","stdin":"","expect":"7\n3\n5\nTrue","hidden":false}],"require":[],"forbid":[],"lines":["7","3","5","True"]},{"task":"The file `rain.txt` has one number on each line: how many millimeters of rain fell on one day. Write a program that reads the file and prints the number of days, the number of dry days (days with 0 mm) and the total rainfall.\n\nWith this file\n\n```text\n0\n5\n12\n0\n4\n0\n2\n```\n\nthe program prints:\n\n```\nDays: 7\nDry days: 3\nTotal: 23 mm\n```\n","kind":"code","seed":"days = 0\ndry_days = 0\ntotal = 0\n","solution":"days = 0\ndry_days = 0\ntotal = 0\nwith open(\"rain.txt\") as file:\n    for line in file:\n        rain = int(line.strip())\n        days += 1\n        if rain == 0:\n            dry_days += 1\n        total += rain\nprint(\"Days:\", days)\nprint(\"Dry days:\", dry_days)\nprint(\"Total:\", total, \"mm\")\n","hints":["Go through the file with `for line in file:` and turn each line into a number: `int(line.strip())`.","Each round adds one to `days`, adds the number to `total`, and adds one to `dry_days` when the number is 0.","Print the three results after the loop, outside the `with` block."],"tests":[{"name":"Test 1","stdin":"","files":{"rain.txt":"0\n5\n12\n0\n4\n0\n2\n"},"expect":"Days: 7\nDry days: 3\nTotal: 23 mm","hidden":false},{"name":"Test 2","stdin":"","files":{"rain.txt":"3\n0\n8\n"},"expect":"Days: 3\nDry days: 1\nTotal: 11 mm","hidden":true},{"name":"Test 3","stdin":"","files":{"rain.txt":"1\n1\n10\n7\n"},"expect":"Days: 4\nDry days: 0\nTotal: 19 mm","hidden":true}],"require":[],"forbid":[]}]}