{"id":"writing-files-appending","slug":"adding-to-a-file","title":"Adding to a file","text":"### Writing replaces\n\nA workout log gets a new line after every workout. Here's `workouts.txt` after two of them, and a program that should add Friday's yoga to it, written with what step 1 taught. It then reads the file back:\n\n```file workouts.txt\nMon: run, 5 km\nWed: swim, 40 min\n```\n\n```java\nimport java.io.PrintWriter;\nimport java.nio.file.Paths;\nimport java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) throws Exception {\n        PrintWriter writer = new PrintWriter(\"workouts.txt\");\n        writer.println(\"Fri: yoga, 30 min\");\n        writer.close();\n\n        try (Scanner reader = new Scanner(Paths.get(\"workouts.txt\"))) {\n            while (reader.hasNextLine()) {\n                System.out.println(reader.nextLine());\n            }\n        }\n    }\n}\n```\n\n```output\nFri: yoga, 30 min\n```\n\nMonday's run and Wednesday's swim are gone: creating the PrintWriter emptied the file before anything was written. That's right for a file the program writes from scratch, but a log should grow.\n\n### FileWriter, which can append\n\n`FileWriter`, also in `java.io`, writes into a file as well, and its constructor takes a second parameter: `new FileWriter(\"workouts.txt\", true)`. `true` means **append**: the file keeps what it holds, and the new text goes after it. When the file doesn't exist yet, it's created, as before.\n\nA FileWriter has no `print` or `println`. Its `write` methods write the text as it is, with no line break: `write(\"Fri: yoga, 30 min\\n\")` needs the `\\n` at the end. Give the FileWriter to a PrintWriter's constructor instead, and you get `println` and `print`, which write through the FileWriter into the file:\n\n```file workouts.txt\nMon: run, 5 km\nWed: swim, 40 min\n```\n\n```java\nimport java.io.FileWriter;\nimport java.io.PrintWriter;\nimport java.nio.file.Paths;\nimport java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) throws Exception {\n        PrintWriter writer = new PrintWriter(new FileWriter(\"workouts.txt\", true));\n        writer.println(\"Fri: yoga, 30 min\");\n        writer.close();\n\n        try (Scanner reader = new Scanner(Paths.get(\"workouts.txt\"))) {\n            while (reader.hasNextLine()) {\n                System.out.println(reader.nextLine());\n            }\n        }\n    }\n}\n```\n\n```output\nMon: run, 5 km\nWed: swim, 40 min\nFri: yoga, 30 min\n```\n\n- `new PrintWriter(new FileWriter(\"workouts.txt\", true))` makes two objects. The FileWriter opens the file for appending, and the PrintWriter writes into the FileWriter. Closing the PrintWriter closes the FileWriter too.\n- Without the `true`, or with `false`, a FileWriter empties the file first, just as a PrintWriter does.\n- FileWriter's constructor can throw an IOException (step 1's FileNotFoundException is one kind of it), so it needs `throws` or `catch` too. A `catch (IOException e)` block, with `import java.io.IOException;`, catches every kind.\n- Every run adds one line. Run the program three times on your own computer, and the file ends with three yoga lines. Each check on Java Arena starts again from the files the test gives, so there each run adds exactly one.\n\n### Append or replace?\n\n- A log, a diary or a list of events grows: each run adds to it, so the program appends.\n- A file that holds all of a program's data, such as a to-do list or a high-score table, is written again from the start each time, so it's replaced. Appending there would write the old contents into the file a second time. Step 4 is about such files.\n","task":"A reading log. The program reads the title of a book you've just finished, adds it as a new line at the end of `books.txt`, and prints `Added: ` and the title. Fill in the blanks, so that the titles already in the file stay, and the file is created if there isn't one yet. For the input `Dune` and the file shown below, it prints:\n\n```\nAdded: Dune\n```\n\nand `books.txt` then ends with the line `Dune`.\n","kind":"fill","seed":"import java.io.FileWriter;\nimport java.io.PrintWriter;\nimport java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) throws Exception {\n        Scanner scanner = new Scanner(System.in);\n        String title = scanner.nextLine();\n        PrintWriter writer = new PrintWriter(new [[FileWriter]](\"books.txt\", [[true]]));\n        writer.println(title);\n        writer.close();\n        System.out.println(\"Added: \" + title);\n    }\n}\n","solution":"import java.io.FileWriter;\nimport java.io.PrintWriter;\nimport java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) throws Exception {\n        Scanner scanner = new Scanner(System.in);\n        String title = scanner.nextLine();\n        PrintWriter writer = new PrintWriter(new FileWriter(\"books.txt\", true));\n        writer.println(title);\n        writer.close();\n        System.out.println(\"Added: \" + title);\n    }\n}\n","hints":["The class that can append to a file is `FileWriter`, and the PrintWriter writes through it.","FileWriter's second parameter says whether to append: `true` keeps what the file holds and writes after it."],"tests":[{"name":"Test 1","stdin":"Dune\n","files":{"books.txt":"The Hobbit\nBeloved\n"},"writes":{"books.txt":"The Hobbit\nBeloved\nDune"},"expect":"Added: Dune","hidden":false},{"name":"Without the file","stdin":"Emma\n","writes":{"books.txt":"Emma"},"expect":"Added: Emma","hidden":true},{"name":"Test 3","stdin":"Kindred\n","files":{"books.txt":"Matilda\nUlysses\nFrankenstein\n"},"writes":{"books.txt":"Matilda\nUlysses\nFrankenstein\nKindred"},"expect":"Added: Kindred","hidden":true}],"require":[],"forbid":[],"more":[{"task":"A calculator keeps its history in `history.txt`, and its method `save` writes a line there, appending or not. The folder is empty when the program starts. What does the program print?\n","kind":"predict","seed":"import java.io.FileWriter;\nimport java.io.PrintWriter;\nimport java.nio.file.Paths;\nimport java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) throws Exception {\n        save(\"2 + 2 = 4\", true);\n        save(\"5 * 3 = 15\", true);\n        save(\"9 / 3 = 3\", false);\n        save(\"7 + 1 = 8\", true);\n\n        try (Scanner reader = new Scanner(Paths.get(\"history.txt\"))) {\n            while (reader.hasNextLine()) {\n                System.out.println(reader.nextLine());\n            }\n        }\n    }\n\n    public static void save(String line, boolean append) throws Exception {\n        PrintWriter writer = new PrintWriter(new FileWriter(\"history.txt\", append));\n        writer.println(line);\n        writer.close();\n    }\n}\n","solution":"import java.io.FileWriter;\nimport java.io.PrintWriter;\nimport java.nio.file.Paths;\nimport java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) throws Exception {\n        save(\"2 + 2 = 4\", true);\n        save(\"5 * 3 = 15\", true);\n        save(\"9 / 3 = 3\", false);\n        save(\"7 + 1 = 8\", true);\n\n        try (Scanner reader = new Scanner(Paths.get(\"history.txt\"))) {\n            while (reader.hasNextLine()) {\n                System.out.println(reader.nextLine());\n            }\n        }\n    }\n\n    public static void save(String line, boolean append) throws Exception {\n        PrintWriter writer = new PrintWriter(new FileWriter(\"history.txt\", append));\n        writer.println(line);\n        writer.close();\n    }\n}\n","hints":["`new FileWriter(\"history.txt\", true)` creates the file when it isn't there yet, and otherwise adds after what it holds. With `false`, it empties the file first.","The third call empties the file, so the first two lines are gone. Only one call comes after it."],"tests":[{"name":"Output","stdin":"","expect":"9 / 3 = 3\n7 + 1 = 8","hidden":false}],"require":[],"forbid":[],"lines":["9 / 3 = 3","7 + 1 = 8"]},{"task":"A sleep tracker adds a line to its log every morning. Write the method `logNight(String fileName, String day, int hours)`, which adds the line `day: hours h`, such as `Tue: 7 h`, at the end of the file, keeping what the file holds. When there's no such file yet, it's created. Use a FileWriter that appends.\n\nNobody sleeps less than 0 or more than 24 hours in a night. For such hours, `logNight` throws an IllegalArgumentException with the message `impossible: ` and the hours and ` hours`, such as `impossible: 30 hours`, and writes nothing.\n\nThe check calls `logNight` for Tuesday (7 hours) and Wednesday (6 hours) on the file shown below, which has Monday's line, and after it `sleep.txt` should hold:\n\n```text\nMon: 8 h\nTue: 7 h\nWed: 6 h\n```\n","kind":"code","seed":"import java.io.FileWriter;\nimport java.io.PrintWriter;\n\npublic class Main {\n    public static void main(String[] args) throws Exception {\n        logNight(\"sleep.txt\", \"Tue\", 7);\n        logNight(\"sleep.txt\", \"Wed\", 6);\n    }\n\n    // Write the method logNight here.\n}\n","solution":"import java.io.FileWriter;\nimport java.io.PrintWriter;\n\npublic class Main {\n    public static void main(String[] args) throws Exception {\n        logNight(\"sleep.txt\", \"Tue\", 7);\n        logNight(\"sleep.txt\", \"Wed\", 6);\n    }\n\n    public static void logNight(String fileName, String day, int hours) throws Exception {\n        if (hours < 0 || hours > 24) {\n            throw new IllegalArgumentException(\"impossible: \" + hours + \" hours\");\n        }\n        PrintWriter writer = new PrintWriter(new FileWriter(fileName, true));\n        writer.println(day + \": \" + hours + \" h\");\n        writer.close();\n    }\n}\n","hints":["Check the hours first, and `throw new IllegalArgumentException(\"impossible: \" + hours + \" hours\")` before the file is opened. The header ends with `throws Exception`, for the FileWriter.","Then `PrintWriter writer = new PrintWriter(new FileWriter(fileName, true));`, one `println` with the day, `: `, the hours and ` h`, and `writer.close()`."],"tests":[{"name":"Test 1","stdin":"","call":"logNight(\"sleep.txt\", \"Tue\", 7);\nlogNight(\"sleep.txt\", \"Wed\", 6);\n","files":{"sleep.txt":"Mon: 8 h\n"},"writes":{"sleep.txt":"Mon: 8 h\nTue: 7 h\nWed: 6 h"},"expect":"","hidden":false},{"name":"logNight(\"nights.txt\", \"Sun\", 9);","stdin":"","call":"logNight(\"nights.txt\", \"Sun\", 9);\n","writes":{"nights.txt":"Sun: 9 h"},"expect":"","hidden":true},{"name":"Test 3","stdin":"","call":"try {\n    logNight(\"sleep.txt\", \"Thu\", 30);\n} catch (IllegalArgumentException e) {\n    System.out.println(e.getMessage());\n}\nlogNight(\"sleep.txt\", \"Fri\", 0);\ntry {\n    logNight(\"sleep.txt\", \"Sat\", -2);\n} catch (IllegalArgumentException e) {\n    System.out.println(e.getMessage());\n}\nlogNight(\"sleep.txt\", \"Sun\", 24);\n","files":{"sleep.txt":"Wed: 5 h\nThu: 6 h\n"},"writes":{"sleep.txt":"Wed: 5 h\nThu: 6 h\nFri: 0 h\nSun: 24 h"},"expect":"impossible: 30 hours\nimpossible: -2 hours","hidden":true}],"require":[{"pattern":"new\\s+FileWriter\\s*\\([^;]*,\\s*true\\s*\\)","message":"Append with a FileWriter: `new FileWriter(fileName, true)`."}],"forbid":[]}]}