{"id":"writing-files-saving-loading","slug":"saving-and-loading","title":"Saving and loading","text":"### save and load\n\nA program that writes its data when it ends, and reads it back when it starts, remembers the data from one run to the next. The data usually lives in an object, so that object's class gets two methods: `save` writes everything the object holds into a file, and `load` reads such a file back into the object. The two must agree on the format: `load` has to understand whatever `save` writes.\n\nHere's a to-do list. It keeps its tasks in a list of Strings, and its file has one task per line:\n\n```java\npublic class Main {\n    public static void main(String[] args) throws Exception {\n        TodoList monday = new TodoList();\n        monday.add(\"Water the ferns\");\n        monday.add(\"Pay the phone bill\");\n        monday.save(\"todo.txt\");\n\n        TodoList tuesday = new TodoList();\n        tuesday.load(\"todo.txt\");\n        tuesday.add(\"Return the library books\");\n        tuesday.print();\n    }\n}\n// ==== TodoList.java ====\nimport java.io.PrintWriter;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.Scanner;\n\npublic class TodoList {\n    private ArrayList<String> tasks;\n\n    public TodoList() {\n        this.tasks = new ArrayList<>();\n    }\n\n    public void add(String task) {\n        this.tasks.add(task);\n    }\n\n    public void print() {\n        for (int i = 0; i < this.tasks.size(); i++) {\n            System.out.println((i + 1) + \". \" + this.tasks.get(i));\n        }\n    }\n\n    public void save(String fileName) throws Exception {\n        try (PrintWriter writer = new PrintWriter(fileName)) {\n            for (String task : this.tasks) {\n                writer.println(task);\n            }\n        }\n    }\n\n    public void load(String fileName) throws Exception {\n        try (Scanner reader = new Scanner(Paths.get(fileName))) {\n            while (reader.hasNextLine()) {\n                String line = reader.nextLine();\n                if (!line.isEmpty()) {\n                    this.tasks.add(line);\n                }\n            }\n        }\n    }\n}\n```\n\n```output\n1. Water the ferns\n2. Pay the phone bill\n3. Return the library books\n```\n\n- `tuesday` is a new object, and its list starts empty. Its first two tasks come from the file, as they would in a program started again the next day.\n- `save` writes the whole list every time, with a PrintWriter that replaces what the file held, so the file always matches the list. Appending would put every old task into the file a second time.\n- `load` adds each line of the file to the list. It skips empty lines, which a person who edits the file by hand may leave behind.\n- Both methods pass their exceptions on with `throws Exception`. The class can't know what a program wants to do when its file can't be read or written, so the code that calls them decides, as module 42 said.\n\n### The first run\n\nThe very first time the program runs, there's no `todo.txt` yet, and `load`'s Scanner throws a NoSuchFileException. For a to-do app that isn't a problem: it just starts with an empty list. So the program catches the exception around `load`, and goes on:\n\n```java\nTodoList list = new TodoList();\ntry {\n    list.load(\"todo.txt\");\n} catch (Exception e) {\n    System.out.println(\"No saved tasks yet\");\n}\n```\n\n### Load when it starts, save when it ends\n\nPut together with a loop that reads commands (module 25), this gives a to-do app that remembers its tasks. `main` loads the list, the loop runs the commands, and after `quit`, `main` saves the list:\n\n```file todo.txt\nWater the ferns\nPay the phone bill\n```\n\n```java\nimport java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        TodoList list = new TodoList();\n        try {\n            list.load(\"todo.txt\");\n        } catch (Exception e) {\n            System.out.println(\"No saved tasks yet\");\n        }\n\n        Scanner scanner = new Scanner(System.in);\n        while (true) {\n            String command = scanner.nextLine();\n            if (command.equals(\"quit\")) {\n                break;\n            }\n            if (command.equals(\"add\")) {\n                list.add(scanner.nextLine());\n            } else if (command.equals(\"list\")) {\n                list.print();\n            } else {\n                System.out.println(\"Unknown command: \" + command);\n            }\n        }\n\n        try {\n            list.save(\"todo.txt\");\n            System.out.println(\"Saved\");\n        } catch (Exception e) {\n            System.out.println(\"Could not save: \" + e.getMessage());\n        }\n    }\n}\n// ==== TodoList.java ====\nimport java.io.PrintWriter;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.Scanner;\n\npublic class TodoList {\n    private ArrayList<String> tasks;\n\n    public TodoList() {\n        this.tasks = new ArrayList<>();\n    }\n\n    public void add(String task) {\n        this.tasks.add(task);\n    }\n\n    public void print() {\n        for (int i = 0; i < this.tasks.size(); i++) {\n            System.out.println((i + 1) + \". \" + this.tasks.get(i));\n        }\n    }\n\n    public void save(String fileName) throws Exception {\n        try (PrintWriter writer = new PrintWriter(fileName)) {\n            for (String task : this.tasks) {\n                writer.println(task);\n            }\n        }\n    }\n\n    public void load(String fileName) throws Exception {\n        try (Scanner reader = new Scanner(Paths.get(fileName))) {\n            while (reader.hasNextLine()) {\n                String line = reader.nextLine();\n                if (!line.isEmpty()) {\n                    this.tasks.add(line);\n                }\n            }\n        }\n    }\n}\n```\n\n```input\nadd\nBuy birdseed\nlist\nquit\n```\n\n```output\n1. Water the ferns\n2. Pay the phone bill\n3. Buy birdseed\nSaved\n```\n\nAfter the run, `todo.txt` holds the three tasks, and the next time the program starts, it has them all. In a larger program the command loop would be in a user interface class of its own, as in module 25, and `main` would still load at the start and save at the end.\n","task":"Step 3's high scores get a class that keeps them. `HighScore.java` is step 3's class, and `ScoreBoard` keeps a list of HighScores, with `add` and `best()` ready. Write ScoreBoard's methods `save(String fileName)` and `load(String fileName)`:\n\n- `save` writes every score into the file as `player,points`, one per line, in the list's order, replacing what the file held.\n- `load` reads such a file, and adds a HighScore for each of its lines to the board.\n- Both pass exceptions on with `throws Exception`.\n\nThe check loads the file shown below, adds Kai's 5100 points, saves the board into the same file and prints the best score:\n\n```\nKai: 5100\n```\n\nand `scores.csv` then holds all three scores.\n","kind":"code","seed":"// ==== ScoreBoard.java ====\nimport java.io.PrintWriter;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.Scanner;\n\npublic class ScoreBoard {\n    private ArrayList<HighScore> scores;\n\n    public ScoreBoard() {\n        this.scores = new ArrayList<>();\n    }\n\n    public void add(HighScore score) {\n        this.scores.add(score);\n    }\n\n    public HighScore best() {\n        HighScore best = null;\n        for (HighScore score : this.scores) {\n            if (best == null || score.getPoints() > best.getPoints()) {\n                best = score;\n            }\n        }\n        return best;\n    }\n\n    // Write the methods save and load here.\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) throws Exception {\n        ScoreBoard board = new ScoreBoard();\n        board.load(\"scores.csv\");\n        board.add(new HighScore(\"Kai\", 5100));\n        board.save(\"scores.csv\");\n        System.out.println(board.best());\n    }\n}\n\n// ==== HighScore.java ====\npublic class HighScore {\n    private String player;\n    private int points;\n\n    public HighScore(String player, int points) {\n        this.player = player;\n        this.points = points;\n    }\n\n    public String getPlayer() {\n        return this.player;\n    }\n\n    public int getPoints() {\n        return this.points;\n    }\n\n    public String toCsv() {\n        return this.player + \",\" + this.points;\n    }\n\n    @Override\n    public String toString() {\n        return this.player + \": \" + this.points;\n    }\n}\n","solution":"// ==== ScoreBoard.java ====\nimport java.io.PrintWriter;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.Scanner;\n\npublic class ScoreBoard {\n    private ArrayList<HighScore> scores;\n\n    public ScoreBoard() {\n        this.scores = new ArrayList<>();\n    }\n\n    public void add(HighScore score) {\n        this.scores.add(score);\n    }\n\n    public HighScore best() {\n        HighScore best = null;\n        for (HighScore score : this.scores) {\n            if (best == null || score.getPoints() > best.getPoints()) {\n                best = score;\n            }\n        }\n        return best;\n    }\n\n    public void save(String fileName) throws Exception {\n        try (PrintWriter writer = new PrintWriter(fileName)) {\n            for (HighScore score : this.scores) {\n                writer.println(score.toCsv());\n            }\n        }\n    }\n\n    public void load(String fileName) throws Exception {\n        try (Scanner reader = new Scanner(Paths.get(fileName))) {\n            while (reader.hasNextLine()) {\n                String[] parts = reader.nextLine().split(\",\");\n                this.scores.add(new HighScore(parts[0], Integer.valueOf(parts[1])));\n            }\n        }\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) throws Exception {\n        ScoreBoard board = new ScoreBoard();\n        board.load(\"scores.csv\");\n        board.add(new HighScore(\"Kai\", 5100));\n        board.save(\"scores.csv\");\n        System.out.println(board.best());\n    }\n}\n\n// ==== HighScore.java ====\npublic class HighScore {\n    private String player;\n    private int points;\n\n    public HighScore(String player, int points) {\n        this.player = player;\n        this.points = points;\n    }\n\n    public String getPlayer() {\n        return this.player;\n    }\n\n    public int getPoints() {\n        return this.points;\n    }\n\n    public String toCsv() {\n        return this.player + \",\" + this.points;\n    }\n\n    @Override\n    public String toString() {\n        return this.player + \": \" + this.points;\n    }\n}\n","hints":["`save` is step 3's loop in a try-with-resources, like TodoList's `save` above, writing each score's `toCsv()`. `load` is module 20's reading loop, like TodoList's `load`, splitting each line at the comma.","`save`: `try (PrintWriter writer = new PrintWriter(fileName)) { for (HighScore score : this.scores) { writer.println(score.toCsv()); } }`. `load` reads with `try (Scanner reader = new Scanner(Paths.get(fileName)))`, splits each line with `split(\",\")`, and adds `new HighScore(parts[0], Integer.valueOf(parts[1]))` to `this.scores`."],"tests":[{"name":"Test 1","stdin":"","call":"ScoreBoard board = new ScoreBoard();\nboard.load(\"scores.csv\");\nboard.add(new HighScore(\"Kai\", 5100));\nboard.save(\"scores.csv\");\nSystem.out.println(board.best());\n","files":{"scores.csv":"Mia,4200\nLeo,3950\n"},"writes":{"scores.csv":"Mia,4200\nLeo,3950\nKai,5100"},"expect":"Kai: 5100","hidden":false},{"name":"Test 2","stdin":"","call":"ScoreBoard board = new ScoreBoard();\nboard.load(\"scores.csv\");\nSystem.out.println(board.best());\nboard.save(\"copy.csv\");\n","files":{"scores.csv":"Ana,3100\nMia,4200\nLeo,3950\nTove,12\n"},"writes":{"copy.csv":"Ana,3100\nMia,4200\nLeo,3950\nTove,12"},"expect":"Mia: 4200","hidden":true},{"name":"Test 3","stdin":"","call":"ScoreBoard board = new ScoreBoard();\nboard.add(new HighScore(\"Rui\", 700));\nboard.add(new HighScore(\"Sol\", 900));\nboard.save(\"new.csv\");\nboard.add(new HighScore(\"Tam\", 800));\nboard.save(\"new.csv\");\nScoreBoard other = new ScoreBoard();\nother.load(\"new.csv\");\nSystem.out.println(other.best());\n","writes":{"new.csv":"Rui,700\nSol,900\nTam,800"},"expect":"Sol: 900","hidden":true}],"require":[{"pattern":"\\bvoid\\s+save\\s*\\([^)]*\\)\\s*throws\\b","message":"`save` passes exceptions on: `public void save(String fileName) throws Exception`."},{"pattern":"\\bvoid\\s+load\\s*\\([^)]*\\)\\s*throws\\b","message":"`load` passes exceptions on: `public void load(String fileName) throws Exception`."}],"forbid":[],"more":[{"task":"A program keeps count of how many times it has been run, in the file `runs.txt`, which holds one number. Write `main`: it reads the number from `runs.txt`, adds one, writes the new number back into the file, replacing the old one, and prints `Run number ` and the new number. When there's no `runs.txt` yet, this is the first run, and the count starts from 0. With the file shown below, the program prints:\n\n```\nRun number 4\n```\n\nand `runs.txt` then holds `4`.\n","kind":"code","seed":"import java.io.PrintWriter;\nimport java.nio.file.Paths;\nimport java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n\n    }\n}\n","solution":"import 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        int runs = 0;\n        try (Scanner reader = new Scanner(Paths.get(\"runs.txt\"))) {\n            runs = Integer.valueOf(reader.nextLine());\n        } catch (Exception e) {\n            // No file yet: this is the first run.\n        }\n        runs++;\n        try (PrintWriter writer = new PrintWriter(\"runs.txt\")) {\n            writer.println(runs);\n        }\n        System.out.println(\"Run number \" + runs);\n    }\n}\n","hints":["Start with `int runs = 0;`. Read the number in a try-with-resources with a Scanner, `runs = Integer.valueOf(reader.nextLine());`, and let the `catch` block keep the 0: no file means the first run.","Then `runs++`, write it with a PrintWriter, which replaces the old number, and print the line. `main` needs `throws Exception` for the PrintWriter, or a `try` of its own."],"tests":[{"name":"Test 1","stdin":"","files":{"runs.txt":"3\n"},"writes":{"runs.txt":"4"},"expect":"Run number 4","hidden":false},{"name":"The first run, without the file","stdin":"","writes":{"runs.txt":"1"},"expect":"Run number 1","hidden":true},{"name":"Test 3","stdin":"","files":{"runs.txt":"41\n"},"writes":{"runs.txt":"42"},"expect":"Run number 42","hidden":true},{"name":"Test 4","stdin":"","files":{"runs.txt":"999\n"},"writes":{"runs.txt":"1000"},"expect":"Run number 1000","hidden":true}],"require":[],"forbid":[]},{"task":"A wish list app saves its wishes when it ends. The output looks right, but after a few runs the file is full of the same wishes over and over. Find the bug in `Wishlist.java` and fix it, so that the file holds each wish once. With the file shown below, the program prints:\n\n```\n3 wishes saved\n```\n\nand `wishes.txt` should then hold `kite`, `bike` and `board game`, one per line.\n","kind":"code","seed":"// ==== Wishlist.java ====\nimport java.io.FileWriter;\nimport java.io.PrintWriter;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.Scanner;\n\npublic class Wishlist {\n    private ArrayList<String> wishes;\n\n    public Wishlist() {\n        this.wishes = new ArrayList<>();\n    }\n\n    public void add(String wish) {\n        this.wishes.add(wish);\n    }\n\n    public int size() {\n        return this.wishes.size();\n    }\n\n    public void save(String fileName) throws Exception {\n        try (PrintWriter writer = new PrintWriter(new FileWriter(fileName, true))) {\n            for (String wish : this.wishes) {\n                writer.println(wish);\n            }\n        }\n    }\n\n    public void load(String fileName) throws Exception {\n        try (Scanner reader = new Scanner(Paths.get(fileName))) {\n            while (reader.hasNextLine()) {\n                this.wishes.add(reader.nextLine());\n            }\n        }\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) throws Exception {\n        Wishlist list = new Wishlist();\n        list.load(\"wishes.txt\");\n        list.add(\"board game\");\n        list.save(\"wishes.txt\");\n        System.out.println(list.size() + \" wishes saved\");\n    }\n}\n","solution":"// ==== Wishlist.java ====\nimport java.io.PrintWriter;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.Scanner;\n\npublic class Wishlist {\n    private ArrayList<String> wishes;\n\n    public Wishlist() {\n        this.wishes = new ArrayList<>();\n    }\n\n    public void add(String wish) {\n        this.wishes.add(wish);\n    }\n\n    public int size() {\n        return this.wishes.size();\n    }\n\n    public void save(String fileName) throws Exception {\n        try (PrintWriter writer = new PrintWriter(fileName)) {\n            for (String wish : this.wishes) {\n                writer.println(wish);\n            }\n        }\n    }\n\n    public void load(String fileName) throws Exception {\n        try (Scanner reader = new Scanner(Paths.get(fileName))) {\n            while (reader.hasNextLine()) {\n                this.wishes.add(reader.nextLine());\n            }\n        }\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) throws Exception {\n        Wishlist list = new Wishlist();\n        list.load(\"wishes.txt\");\n        list.add(\"board game\");\n        list.save(\"wishes.txt\");\n        System.out.println(list.size() + \" wishes saved\");\n    }\n}\n","hints":["The output is right, and the file isn't. Look at how `save` opens the file: what does the `true` do there?","`save` writes the whole list, the wishes that `load` read from the file included, so the file must be emptied first: `new PrintWriter(fileName)`, without the FileWriter."],"tests":[{"name":"Test 1","stdin":"","files":{"wishes.txt":"kite\nbike\n"},"writes":{"wishes.txt":"kite\nbike\nboard game"},"expect":"3 wishes saved","hidden":false},{"name":"Test 2","stdin":"","call":"Wishlist list = new Wishlist();\nlist.add(\"puzzle\");\nlist.save(\"mine.txt\");\nlist.add(\"skates\");\nlist.save(\"mine.txt\");\nSystem.out.println(list.size());\n","writes":{"mine.txt":"puzzle\nskates"},"expect":"2","hidden":true},{"name":"Test 3","stdin":"","files":{"wishes.txt":"telescope\n"},"writes":{"wishes.txt":"telescope\nboard game"},"expect":"2 wishes saved","hidden":true}],"require":[],"forbid":[]}]}