Skip to Content
Course content

97: Writing Files in Kotlin

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I was tinkering with a small RPG project last night and realized I had no way to actually save the player's progress. Everything lived in memory, so every time I restarted the app, my level 50 Paladin vanished. I figured it was time to get some data onto the disk.

The Quick and Dirty Way

When I first started, I didn't want to mess around with complex streams or buffers. I just wanted my stats in a file. Since Kotlin provides some great extension functions for java.io.File, I tried the most direct approach first.

val saveFile = File("player_save.txt")
saveFile.writeText("Level: 50\nClass: Paladin\nGold: 1200")
println("Game saved!")

This worked exactly as expected. I checked the folder, and there was my file. writeText is incredibly convenient because it handles opening the stream and closing it for you. But then I ran into a problem. I wanted to add a "Last Played" timestamp to the file without deleting the existing stats.

Wait, where did my data go?

I tried calling writeText again with the timestamp, but when I opened the file, the Level, Class, and Gold were gone. Only the timestamp remained. I realized writeText doesn't add to the file; it completely overwrites it. That's a dangerous habit to get into if you're building a log or a history file.

I needed a way to just "tack on" new information. I looked through the extension functions and found appendText. Let's see how that behaves:

val saveFile = File("player_save.txt")
saveFile.appendText("\nLast Played: 2023-10-27")
println("Timestamp added!")

Now the file contains both the original stats and the date. Much better. However, I started thinking about the "Game Log"—a feature where every single action the player takes is recorded. If I use appendText every time the player moves or attacks, I'm asking the OS to open the file, find the end, write a few bytes, and close the file... hundreds of times per minute. That's a recipe for a laggy game.

Scaling up for the long haul

For high-frequency writing, we need a buffer. Instead of hitting the disk for every single string, a buffer collects data in memory and writes it in one larger chunk. Kotlin gives us bufferedWriter() for this.

I tried implementing the game log this way, but I noticed something weird: if I just called bufferedWriter() and wrote to it, nothing appeared in the file until the program ended. I forgot that buffers need to be "flushed" or closed to ensure the data actually moves from RAM to the disk.

The cleanest way to handle this is using .use { }. This ensures the writer is closed automatically, even if the code throws an exception. Here is how I settled on doing the logging:

val logFile = File("game_log.txt")

logFile.bufferedWriter().use { out ->
    out.write("Player entered the Dark Forest\n")
    out.write("Player encountered a Goblin\n")
    out.write("Player dealt 15 damage\n")
}

I've found that writeText is perfect for config files, appendText is great for occasional updates, and bufferedWriter is the only way to go when you're dealing with loops or large amounts of data. If you don't use that .use block, you're basically gambling with your data.




📋 Practical Task

Build a Player Inventory Persistence System

Create a program that simulates saving a player's inventory to a file. You should start with a list of items (e.g., "Iron Sword", "Health Potion", "Ancient Map").

  • Create a File object named inventory.txt.
  • Use a bufferedWriter within a .use block to iterate through your list of items and write each one on a new line.
  • After writing the inventory, use appendText to add a single line at the bottom of the file indicating the total number of items saved.
  • Verify that the file contains both the individual items and the final count.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.