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
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+)
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
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
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
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
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
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
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
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
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
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
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
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:
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
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
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
-
Create a program that reads a text file, counts the occurrences of each word, and writes the results to a new file.
-
Build a directory scanner that lists all files above a certain size.
-
Create a simple file backup system that copies modified files from one directory to another, preserving their directory structure.
-
Implement a file-based key-value store where users can save and retrieve values by key.
-
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! :)