Skip to main content

Kotlin File Management

Introduction

File management is a crucial aspect of any programming language, allowing applications to store and retrieve data persistently. Kotlin provides robust and straightforward ways to handle files and directories through its standard library and Java interoperability.

In this tutorial, we'll explore how to perform various file operations in Kotlin, including:

  • Creating files and directories
  • Reading from files
  • Writing to files
  • Checking if files exist
  • Deleting files
  • Listing directory contents
  • Working with file paths

Whether you're building a simple text editor or a complex application that needs to store configuration data, understanding file management in Kotlin will be essential for your programming journey.

Basic File Operations

Working with File Paths

In Kotlin, you can work with files using the java.io.File class or the more modern kotlin.io.path API.

Using java.io.File

kotlin
import java.io.File

// Create a file reference
val file = File("example.txt")

// Get absolute path
println("Absolute path: ${file.absolutePath}")

// Check if the file exists
println("Does file exist? ${file.exists()}")

Using kotlin.io.path API (Kotlin 1.5+)

kotlin
import kotlin.io.path.*
import java.nio.file.Path

// Create a path reference
val path = Path("example.txt")

// Get absolute path
println("Absolute path: ${path.absolutePathString()}")

// Check if the file exists
println("Does file exist? ${path.exists()}")

Creating Files and Directories

Creating a New File

kotlin
import java.io.File

fun main() {
val file = File("sample.txt")

val isCreated = file.createNewFile()

if (isCreated) {
println("File created successfully at: ${file.absolutePath}")
} else {
println("File already exists at: ${file.absolutePath}")
}
}

Output (if file doesn't exist):

File created successfully at: /path/to/your/project/sample.txt

Creating Directories

kotlin
import java.io.File

fun main() {
// Create a single directory
val singleDir = File("data")
if (singleDir.mkdir()) {
println("Directory created: ${singleDir.absolutePath}")
}

// Create multiple nested directories
val nestedDirs = File("data/images/profiles")
if (nestedDirs.mkdirs()) {
println("Nested directories created: ${nestedDirs.absolutePath}")
}
}

Reading from Files

Reading a File Line by Line

kotlin
import java.io.File

fun main() {
val file = File("sample.txt")

if (file.exists()) {
// Method 1: Using readLines()
val lines = file.readLines()
println("File contents using readLines():")
lines.forEach { println(it) }

// Method 2: Using forEachLine
println("\nFile contents using forEachLine:")
file.forEachLine { println(it) }
} else {
println("File does not exist")
}
}

Assuming sample.txt contains:

Hello, Kotlin!
File management is fun.
This is the third line.

Output:

File contents using readLines():
Hello, Kotlin!
File management is fun.
This is the third line.

File contents using forEachLine:
Hello, Kotlin!
File management is fun.
This is the third line.

Reading a File as Text

kotlin
import java.io.File

fun main() {
val file = File("sample.txt")

if (file.exists()) {
// Read the entire file as a single string
val content = file.readText()
println("File contents as a single string:")
println(content)

// Read as bytes
val bytes = file.readBytes()
println("\nFile size in bytes: ${bytes.size}")
} else {
println("File does not exist")
}
}

Using BufferedReader for Efficient Reading

kotlin
import java.io.BufferedReader
import java.io.FileReader

fun main() {
val file = "sample.txt"

try {
BufferedReader(FileReader(file)).use { reader ->
var line: String?
while (reader.readLine().also { line = it } != null) {
println(line)
}
}
} catch (e: Exception) {
println("Error reading file: ${e.message}")
}
}

The use function ensures that the reader is closed properly after the reading operation is complete.

Writing to Files

Writing Text to a File

kotlin
import java.io.File

fun main() {
val file = File("output.txt")

// Method 1: Using writeText (overwrites the file)
file.writeText("Hello, this is a test file.\n")
println("Text written to file using writeText()")

// Method 2: Appending to file
file.appendText("This line is appended to the file.\n")
println("Text appended to file using appendText()")

// Read and print the file content to verify
println("\nFile content after writing:")
println(file.readText())
}

Output:

Text written to file using writeText()
Text appended to file using appendText()

File content after writing:
Hello, this is a test file.
This line is appended to the file.

Using BufferedWriter

kotlin
import java.io.BufferedWriter
import java.io.File
import java.io.FileWriter

fun main() {
val file = File("buffered_output.txt")

try {
BufferedWriter(FileWriter(file)).use { writer ->
writer.write("Line 1: Writing with BufferedWriter\n")
writer.write("Line 2: It's efficient for large files\n")
writer.write("Line 3: It uses a buffer to reduce I/O operations\n")
}
println("Successfully wrote to file using BufferedWriter")

// Read and display the content
println("\nFile content:")
file.forEachLine { println(it) }
} catch (e: Exception) {
println("Error writing to file: ${e.message}")
}
}

Managing Files and Directories

Checking if a File or Directory Exists

kotlin
import java.io.File

fun main() {
val file = File("example.txt")
val directory = File("example_directory")

println("File 'example.txt' exists: ${file.exists()}")
println("'example.txt' is a file: ${file.isFile}")

println("Directory 'example_directory' exists: ${directory.exists()}")
println("'example_directory' is a directory: ${directory.isDirectory}")
}

Listing Directory Contents

kotlin
import java.io.File

fun main() {
val directory = File(".") // Current directory

println("Contents of ${directory.absolutePath}:")

// List all files and directories
val contents = directory.listFiles()

if (contents != null) {
for (item in contents) {
val type = if (item.isDirectory) "Directory" else "File"
println("${item.name} - $type")
}
} else {
println("Failed to list directory contents or directory is empty")
}

// List only files
println("\nFiles only:")
directory.listFiles { file -> file.isFile }?.forEach { println(it.name) }

// List with a specific extension
println("\nKotlin files only:")
directory.listFiles { file -> file.extension == "kt" }?.forEach { println(it.name) }
}

Moving and Copying Files

kotlin
import java.io.File

fun main() {
// Source file
val sourceFile = File("source.txt")

// Create source file if it doesn't exist
if (!sourceFile.exists()) {
sourceFile.writeText("This is the source file content")
println("Created source file: ${sourceFile.absolutePath}")
}

// Copy file
val copyFile = File("copy.txt")
sourceFile.copyTo(copyFile, overwrite = true)
println("Copied file to: ${copyFile.absolutePath}")

// Rename/Move file
val movedFile = File("moved.txt")
if (copyFile.renameTo(movedFile)) {
println("Moved/Renamed file to: ${movedFile.absolutePath}")
} else {
println("Failed to move/rename file")
}

// Read file content to verify
if (movedFile.exists()) {
println("\nContent of moved file:")
println(movedFile.readText())
}
}

Deleting Files and Directories

kotlin
import java.io.File

fun main() {
// Create a test file
val tempFile = File("temp_file.txt")
tempFile.writeText("This file will be deleted")

// Create a test directory with a file in it
val tempDir = File("temp_dir")
tempDir.mkdir()

val fileInDir = File(tempDir, "file_in_dir.txt")
fileInDir.writeText("This file is inside a directory")

// Delete the file
if (tempFile.delete()) {
println("Successfully deleted file: ${tempFile.name}")
} else {
println("Failed to delete file: ${tempFile.name}")
}

// To delete a directory, it must be empty
if (fileInDir.delete()) {
println("Deleted file inside directory: ${fileInDir.name}")
}

if (tempDir.delete()) {
println("Successfully deleted directory: ${tempDir.name}")
} else {
println("Failed to delete directory: ${tempDir.name}")
}
}

Real-World Examples

Example 1: Simple Log File System

This example demonstrates how to implement a basic logging system:

kotlin
import java.io.File
import java.text.SimpleDateFormat
import java.util.Date

class SimpleLogger(private val logFile: String = "application.log") {
private val file = File(logFile)

init {
if (!file.exists()) {
file.createNewFile()
}
}

fun log(message: String, level: LogLevel = LogLevel.INFO) {
val timestamp = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(Date())
val formattedMessage = "[$timestamp] ${level.name}: $message\n"
file.appendText(formattedMessage)
}

fun readLogs(): List<String> {
return file.readLines()
}

enum class LogLevel {
DEBUG, INFO, WARNING, ERROR
}
}

fun main() {
val logger = SimpleLogger()

logger.log("Application started", SimpleLogger.LogLevel.INFO)
logger.log("Processing user data", SimpleLogger.LogLevel.DEBUG)
logger.log("Network connection lost", SimpleLogger.LogLevel.WARNING)
logger.log("Database query failed", SimpleLogger.LogLevel.ERROR)

println("Log entries:")
logger.readLogs().forEach { println(it) }
}

Example 2: Configuration File Manager

kotlin
import java.io.File
import java.util.Properties

class ConfigManager(private val configFile: String = "config.properties") {
private val properties = Properties()
private val file = File(configFile)

init {
if (file.exists()) {
loadConfiguration()
} else {
// Create default configuration
setProperty("app.name", "MyKotlinApp")
setProperty("app.version", "1.0.0")
setProperty("app.debug", "false")
saveConfiguration()
}
}

fun loadConfiguration() {
file.inputStream().use {
properties.load(it)
}
}

fun saveConfiguration() {
file.outputStream().use {
properties.store(it, "Application Configuration")
}
}

fun getProperty(key: String, defaultValue: String = ""): String {
return properties.getProperty(key, defaultValue)
}

fun setProperty(key: String, value: String) {
properties.setProperty(key, value)
}

fun listAllProperties(): Map<String, String> {
val result = mutableMapOf<String, String>()

for (key in properties.stringPropertyNames()) {
result[key] = properties.getProperty(key)
}

return result
}
}

fun main() {
val config = ConfigManager()

// Read configuration
println("App Name: ${config.getProperty("app.name")}")
println("App Version: ${config.getProperty("app.version")}")
println("Debug Mode: ${config.getProperty("app.debug")}")

// Update configuration
config.setProperty("app.debug", "true")
config.setProperty("user.name", "John")
config.saveConfiguration()

println("\nUpdated Configuration:")
val allProps = config.listAllProperties()
for ((key, value) in allProps) {
println("$key = $value")
}
}

Example 3: File Backup Utility

kotlin
import java.io.File
import java.text.SimpleDateFormat
import java.util.Date

class BackupUtility(private val sourceDir: String, private val backupDir: String) {
private val sourceDirectory = File(sourceDir)
private val backupDirectory = File(backupDir)

init {
if (!sourceDirectory.exists() || !sourceDirectory.isDirectory) {
throw IllegalArgumentException("Source directory does not exist: $sourceDir")
}

if (!backupDirectory.exists()) {
backupDirectory.mkdirs()
}
}

fun createBackup(): String {
val timestamp = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
val backupFolderName = "backup_$timestamp"
val backupFolder = File(backupDirectory, backupFolderName)

backupFolder.mkdir()

var filesCopied = 0
sourceDirectory.walkTopDown().forEach { sourceFile ->
if (sourceFile.isFile) {
val relativePath = sourceFile.relativeTo(sourceDirectory).path
val destinationFile = File(backupFolder, relativePath)

// Create parent directories if they don't exist
destinationFile.parentFile?.mkdirs()

sourceFile.copyTo(destinationFile)
filesCopied++
}
}

return "Backup created at ${backupFolder.absolutePath} with $filesCopied files"
}

fun listBackups(): List<String> {
return backupDirectory.listFiles { file ->
file.isDirectory && file.name.startsWith("backup_")
}?.map { it.name } ?: emptyList()
}
}

fun main() {
try {
// Replace with your actual source and backup directories
val backupUtil = BackupUtility("./source_files", "./backups")

println(backupUtil.createBackup())

println("\nAvailable backups:")
backupUtil.listBackups().forEach { println(it) }
} catch (e: Exception) {
println("Backup failed: ${e.message}")
}
}

Summary

In this tutorial, we covered the essentials of file management in Kotlin:

  • Creating files and directories
  • Reading from files using various methods including line-by-line and as complete text
  • Writing to files, both overwriting and appending
  • Managing files and directories - checking existence, listing contents, moving, copying, and deleting
  • Real-world applications including a simple logger, configuration manager, and backup utility

File I/O operations are fundamental to many applications, whether you're building simple tools or complex systems. Kotlin provides a streamlined API for these operations, making them more accessible through extension functions while still leveraging the power of Java's underlying file system capabilities.

Additional Resources

Exercises

  1. Create a program that reads a text file, counts the occurrences of each word, and writes the results to a new file.

  2. Build a directory scanner that lists all files above a certain size.

  3. Create a simple file backup system that copies modified files from one directory to another, preserving their directory structure.

  4. Implement a file-based key-value store where users can save and retrieve values by key.

  5. Create a utility to merge multiple text files into a single file, adding headers to indicate the source of each section.



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)