File Handling
Overview
File handling is a common requirement in program development. Kotlin provides rich file operation APIs, including file reading and writing, directory operations, and path handling. This chapter will detail how to perform various file operations in Kotlin.
Basic File Operations
File Reading
kotlin
import java.io.File
import java.io.FileNotFoundException
import java.nio.charset.Charset
fun main() {
// Create sample file content
val sampleContent = """
First line content
Second line content
Third line content
Line with Chinese: Hello, Kotlin!
Last line
""".trimIndent()
// Write sample file
val file = File("sample.txt")
file.writeText(sampleContent, Charset.forName("UTF-8"))
println("=== File Reading Examples ===")
// 1. Read entire file content
try {
val content = file.readText(Charset.forName("UTF-8"))
println("Complete file content:")
println(content)
} catch (e: FileNotFoundException) {
println("File not found: ${e.message}")
}
// 2. Read line by line
println("\nRead line by line:")
file.readLines(Charset.forName("UTF-8")).forEachIndexed { index, line ->
println("Line ${index + 1}: $line")
}
// 3. Use sequence to read large files (memory-friendly)
println("\nRead using sequence:")
file.useLines { lines ->
lines.take(3).forEach { line ->
println("Sequence read: $line")
}
}
// 4. Read byte array
val bytes = file.readBytes()
println("\nFile size: ${bytes.size} bytes")
// 5. Safe reading (handle exceptions)
fun safeReadFile(filename: String): String? {
return try {
File(filename).readText()
} catch (e: Exception) {
println("Failed to read file: ${e.message}")
null
}
}
println("\nSafe read existing file:")
safeReadFile("sample.txt")?.let { content ->
println("File length: ${content.length}")
}
println("\nSafe read non-existing file:")
safeReadFile("nonexistent.txt")
// Cleanup
file.delete()
}File Writing
kotlin
import java.io.File
import java.io.FileWriter
import java.io.PrintWriter
import java.nio.charset.StandardCharsets
fun main() {
println("=== File Writing Examples ===")
// 1. Write text content
val outputFile = File("output.txt")
val content = """
This is written content
Contains multiple lines
Current time: ${System.currentTimeMillis()}
""".trimIndent()
outputFile.writeText(content, StandardCharsets.UTF_8)
println("Text writing complete")
// 2. Append content
outputFile.appendText("\nAppended content", StandardCharsets.UTF_8)
println("Content appending complete")
// Verify write result
println("File content:")
println(outputFile.readText())
// 3. Write byte array
val binaryFile = File("binary.dat")
val data = byteArrayOf(0x48, 0x65, 0x6C, 0x6C, 0x6F) // "Hello"
binaryFile.writeBytes(data)
println("Binary file writing complete")
// 4. Write using PrintWriter
val writerFile = File("writer_output.txt")
PrintWriter(writerFile, "UTF-8").use { writer ->
writer.println("Write using PrintWriter")
writer.println("Second line")
writer.printf("Formatted output: %d + %d = %d%n", 1, 2, 3)
}
println("PrintWriter writing complete")
// 5. Write line by line
val linesFile = File("lines.txt")
val lines = listOf(
"First line",
"Second line",
"Third line"
)
linesFile.writeText(lines.joinToString("\n"))
println("Line by line writing complete")
// 6. Safe writing (with exception handling)
fun safeWriteFile(filename: String, content: String): Boolean {
return try {
File(filename).writeText(content)
true
} catch (e: Exception) {
println("Failed to write file: ${e.message}")
false
}
}
val success = safeWriteFile("safe_output.txt", "Safely written content")
println("Safe write result: $success")
// Show all created files
println("\nCreated files:")
listOf("output.txt", "binary.dat", "writer_output.txt", "lines.txt", "safe_output.txt")
.map { File(it) }
.filter { it.exists() }
.forEach { file ->
println("${file.name}: ${file.length()} bytes")
}
// Cleanup files
listOf("output.txt", "binary.dat", "writer_output.txt", "lines.txt", "safe_output.txt")
.forEach { File(it).delete() }
}Directory Operations
Directory Creation and Traversal
kotlin
import java.io.File
fun main() {
println("=== Directory Operations Examples ===")
// 1. Create directory structure
val baseDir = File("test_directory")
val subDir1 = File(baseDir, "subdirectory1")
val subDir2 = File(baseDir, "subdirectory2")
val nestedDir = File(subDir1, "nested")
// Create directories
nestedDir.mkdirs() // Create all necessary parent directories
subDir2.mkdir() // Create single directory
println("Directory creation complete")
// 2. Create some test files
File(baseDir, "root_file.txt").writeText("Root directory file")
File(subDir1, "sub1_file.txt").writeText("Subdirectory 1 file")
File(subDir2, "sub2_file.txt").writeText("Subdirectory 2 file")
File(nestedDir, "nested_file.txt").writeText("Nested directory file")
println("Test file creation complete")
// 3. List directory contents
println("\n=== Directory Contents ===")
fun listDirectory(dir: File, indent: String = "") {
if (!dir.exists() || !dir.isDirectory) {
println("${indent}Directory does not exist or is not a directory: ${dir.name}")
return
}
println("${indent}📁 ${dir.name}/")
dir.listFiles()?.forEach { file ->
if (file.isDirectory) {
listDirectory(file, "$indent ")
} else {
println("$indent 📄 ${file.name} (${file.length()} bytes)")
}
}
}
listDirectory(baseDir)
// 4. Traverse using walkTopDown
println("\n=== Recursive Traversal ===")
baseDir.walkTopDown().forEach { file ->
val type = if (file.isDirectory) "DIR" else "FILE"
val size = if (file.isFile) " (${file.length()} bytes)" else ""
println("$type: ${file.relativeTo(baseDir)}$size")
}
// 5. Filter files
println("\n=== File Filtering ===")
val txtFiles = baseDir.walkTopDown()
.filter { it.isFile && it.extension == "txt" }
.toList()
println("Found .txt files:")
txtFiles.forEach { file ->
println("- ${file.relativeTo(baseDir)}")
}
// 6. Directory statistics
println("\n=== Directory Statistics ===")
val stats = baseDir.walkTopDown().fold(
initial = Triple(0, 0, 0L) // (directory count, file count, total size)
) { (dirs, files, size), file ->
when {
file.isDirectory -> Triple(dirs + 1, files, size)
file.isFile -> Triple(dirs, files + 1, size + file.length())
else -> Triple(dirs, files, size)
}
}
println("Directories: ${stats.first}")
println("Files: ${stats.second}")
println("Total size: ${stats.third} bytes")
// 7. Find specific file
println("\n=== File Search ===")
val foundFile = baseDir.walkTopDown()
.find { it.name == "nested_file.txt" }
foundFile?.let { file ->
println("Found file: ${file.absolutePath}")
println("File content: ${file.readText()}")
}
// Cleanup directory
baseDir.deleteRecursively()
println("\nTest directory cleaned up")
}File and Directory Properties
kotlin
import java.io.File
import java.text.SimpleDateFormat
import java.util.*
fun main() {
println("=== File Properties Examples ===")
// Create test file and directory
val testFile = File("test_file.txt")
val testDir = File("test_dir")
testFile.writeText("This is test file content\nContains multiple lines")
testDir.mkdir()
// 1. Basic properties
println("=== Basic Properties ===")
listOf(testFile, testDir).forEach { file ->
println("Name: ${file.name}")
println("Path: ${file.path}")
println("Absolute path: ${file.absolutePath}")
println("Canonical path: ${file.canonicalPath}")
println("Parent directory: ${file.parent}")
println("Exists: ${file.exists()}")
println("Is file: ${file.isFile}")
println("Is directory: ${file.isDirectory}")
println("Is hidden: ${file.isHidden}")
println()
}
// 2. File size and time
println("=== File Size and Time ===")
val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
println("File: ${testFile.name}")
println("Size: ${testFile.length()} bytes")
println("Last modified: ${dateFormat.format(Date(testFile.lastModified()))}")
// Modify file time
val newTime = System.currentTimeMillis() - 24 * 60 * 60 * 1000 // 24 hours ago
testFile.setLastModified(newTime)
println("Modified time after change: ${dateFormat.format(Date(testFile.lastModified()))}")
// 3. Permission checking
println("\n=== Permission Checking ===")
println("Readable: ${testFile.canRead()}")
println("Writable: ${testFile.canWrite()}")
println("Executable: ${testFile.canExecute()}")
// 4. File extension and name handling
println("\n=== File Name Handling ===")
val files = listOf(
"document.txt",
"image.jpg",
"archive.tar.gz",
"script",
".hidden"
)
files.forEach { filename ->
val file = File(filename)
println("Filename: $filename")
println(" Base name: ${file.nameWithoutExtension}")
println(" Extension: ${file.extension}")
println()
}
// 5. Path operations
println("=== Path Operations ===")
val path1 = File("parent/child/file.txt")
val path2 = File("parent")
println("Relative path: ${path1.relativeTo(path2)}")
println("Resolved path: ${File(path2, "child/file.txt")}")
// 6. Disk space information
println("=== Disk Space ===")
val currentDir = File(".")
println("Total space: ${currentDir.totalSpace / (1024 * 1024 * 1024)} GB")
println("Free space: ${currentDir.freeSpace / (1024 * 1024 * 1024)} GB")
println("Usable space: ${currentDir.usableSpace / (1024 * 1024 * 1024)} GB")
// Cleanup
testFile.delete()
testDir.delete()
println("\nTest files cleaned up")
}Advanced File Operations
File Copy and Move
kotlin
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.nio.file.Files
import java.nio.file.StandardCopyOption
fun main() {
println("=== File Copy and Move Examples ===")
// Create source file
val sourceFile = File("source.txt")
val content = """
This is source file content
Contains multiple lines
For testing copy and move operations
Current time: ${System.currentTimeMillis()}
""".trimIndent()
sourceFile.writeText(content)
println("Source file created: ${sourceFile.name} (${sourceFile.length()} bytes)")
// 1. Copy using Kotlin extension function
println("\n=== Copy using copyTo ===")
val copy1 = File("copy1.txt")
sourceFile.copyTo(copy1, overwrite = true)
println("Copy complete: ${copy1.name}")
println("Content verification: ${copy1.readText() == sourceFile.readText()}")
// 2. Copy using Java NIO
println("\n=== Copy using NIO ===")
val copy2 = File("copy2.txt")
Files.copy(sourceFile.toPath(), copy2.toPath(), StandardCopyOption.REPLACE_EXISTING)
println("NIO copy complete: ${copy2.name}")
// 3. Manual copy (suitable for large files)
println("\n=== Manual Copy ===")
fun copyFileManually(source: File, target: File, bufferSize: Int = 8192) {
FileInputStream(source).use { input ->
FileOutputStream(target).use { output ->
val buffer = ByteArray(bufferSize)
var bytesRead: Int
var totalBytes = 0L
while (input.read(buffer).also { bytesRead = it } != -1) {
output.write(buffer, 0, bytesRead)
totalBytes += bytesRead
}
println("Manual copy complete: $totalBytes bytes")
}
}
}
val copy3 = File("copy3.txt")
copyFileManually(sourceFile, copy3)
// 4. File move
println("\n=== File Move ===")
val moveTarget = File("moved.txt")
// Create file to move
val toMove = File("to_move.txt")
toMove.writeText("This file will be moved")
// Move file
val moveSuccess = toMove.renameTo(moveTarget)
println("Move operation: ${if (moveSuccess) "Success" else "Failed"}")
println("Original file exists: ${toMove.exists()}")
println("Target file exists: ${moveTarget.exists()}")
// 5. Directory copy
println("\n=== Directory Copy ===")
// Create source directory structure
val sourceDir = File("source_dir")
val subDir = File(sourceDir, "subdir")
subDir.mkdirs()
File(sourceDir, "file1.txt").writeText("File 1 content")
File(sourceDir, "file2.txt").writeText("File 2 content")
File(subDir, "file3.txt").writeText("Subdirectory file content")
// Copy directory
fun copyDirectory(source: File, target: File) {
if (source.isDirectory) {
if (!target.exists()) {
target.mkdirs()
}
source.listFiles()?.forEach { file ->
val targetFile = File(target, file.name)
if (file.isDirectory) {
copyDirectory(file, targetFile)
} else {
file.copyTo(targetFile, overwrite = true)
}
}
}
}
val targetDir = File("target_dir")
copyDirectory(sourceDir, targetDir)
println("Directory copy complete")
// Verify directory copy
fun countFiles(dir: File): Int {
return dir.walkTopDown().count { it.isFile }
}
println("Source directory file count: ${countFiles(sourceDir)}")
println("Target directory file count: ${countFiles(targetDir)}")
// 6. Batch operations
println("\n=== Batch File Operations ===")
val batchDir = File("batch_files")
batchDir.mkdir()
// Create multiple files
repeat(5) { i ->
File(batchDir, "batch_$i.txt").writeText("Batch file $i content")
}
// Batch rename
batchDir.listFiles()?.forEach { file ->
if (file.isFile && file.name.startsWith("batch_")) {
val newName = file.name.replace("batch_", "renamed_")
val newFile = File(file.parent, newName)
file.renameTo(newFile)
}
}
println("Batch rename complete")
batchDir.listFiles()?.forEach { file ->
println("- ${file.name}")
}
// Cleanup all test files
println("\n=== Cleanup Files ===")
listOf(sourceFile, copy1, copy2, copy3, moveTarget, sourceDir, targetDir, batchDir)
.forEach { file ->
if (file.exists()) {
if (file.isDirectory) {
file.deleteRecursively()
} else {
file.delete()
}
println("Deleted: ${file.name}")
}
}
}File Monitoring and Temporary Files
kotlin
import java.io.File
import java.nio.file.*
import java.util.concurrent.TimeUnit
import kotlin.concurrent.thread
fun main() {
println("=== File Monitoring and Temporary Files Examples ===")
// 1. Temporary file operations
println("=== Temporary Files ===")
// Create temporary file
val tempFile = File.createTempFile("kotlin_temp", ".txt")
println("Temporary file created: ${tempFile.absolutePath}")
// Write to temporary file
tempFile.writeText("This is temporary file content\nTimestamp: ${System.currentTimeMillis()}")
println("Temporary file size: ${tempFile.length()} bytes")
// Set to delete on program exit
tempFile.deleteOnExit()
// Create temporary directory
val tempDir = Files.createTempDirectory("kotlin_temp_dir").toFile()
println("Temporary directory created: ${tempDir.absolutePath}")
// Create file in temporary directory
val tempFileInDir = File(tempDir, "temp_file.txt")
tempFileInDir.writeText("File in temporary directory")
// 2. File monitoring (simplified version)
println("\n=== File Monitoring ===")
val watchDir = File("watch_directory")
watchDir.mkdir()
// Create monitoring thread
val watchThread = thread {
try {
val watchService = FileSystems.getDefault().newWatchService()
val watchKey = watchDir.toPath().register(
watchService,
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_DELETE,
StandardWatchEventKinds.ENTRY_MODIFY
)
println("Starting to monitor directory: ${watchDir.absolutePath}")
while (!Thread.currentThread().isInterrupted) {
val key = watchService.poll(1, TimeUnit.SECONDS)
if (key != null) {
for (event in key.pollEvents()) {
val kind = event.kind()
val filename = event.context()
when (kind) {
StandardWatchEventKinds.ENTRY_CREATE ->
println("File created: $filename")
StandardWatchEventKinds.ENTRY_DELETE ->
println("File deleted: $filename")
StandardWatchEventKinds.ENTRY_MODIFY ->
println("File modified: $filename")
}
}
key.reset()
}
}
} catch (e: Exception) {
println("Monitoring exception: ${e.message}")
}
}
// Simulate file operations
Thread.sleep(1000)
println("Creating test file...")
val testFile1 = File(watchDir, "test1.txt")
testFile1.writeText("Test file 1")
Thread.sleep(1000)
println("Modifying test file...")
testFile1.appendText("\nAppended content")
Thread.sleep(1000)
println("Creating second file...")
val testFile2 = File(watchDir, "test2.txt")
testFile2.writeText("Test file 2")
Thread.sleep(1000)
println("Deleting file...")
testFile1.delete()
Thread.sleep(2000)
// Stop monitoring
watchThread.interrupt()
watchThread.join(5000)
// 3. File locking (simulated)
println("\n=== File Locking Example ===")
val lockFile = File("locked_file.txt")
lockFile.writeText("This is a locked file")
// Simulate file locking check
fun isFileLocked(file: File): Boolean {
return try {
val channel = FileOutputStream(file, true).channel
val lock = channel.tryLock()
if (lock != null) {
lock.release()
channel.close()
false
} else {
true
}
} catch (e: Exception) {
true
}
}
println("File lock status: ${isFileLocked(lockFile)}")
// 4. File comparison
println("\n=== File Comparison ===")
val file1 = File("compare1.txt")
val file2 = File("compare2.txt")
val file3 = File("compare3.txt")
val content1 = "Same content"
val content2 = "Same content"
val content3 = "Different content"
file1.writeText(content1)
file2.writeText(content2)
file3.writeText(content3)
fun filesEqual(file1: File, file2: File): Boolean {
if (file1.length() != file2.length()) return false
return file1.readBytes().contentEquals(file2.readBytes())
}
println("file1 == file2: ${filesEqual(file1, file2)}")
println("file1 == file3: ${filesEqual(file1, file3)}")
// 5. File checksum
println("\n=== File Checksum ===")
fun calculateChecksum(file: File): String {
val bytes = file.readBytes()
return bytes.fold(0) { acc, byte -> acc + byte.toInt() }.toString(16)
}
println("file1 checksum: ${calculateChecksum(file1)}")
println("file2 checksum: ${calculateChecksum(file2)}")
println("file3 checksum: ${calculateChecksum(file3)}")
// Cleanup files
println("\n=== Cleanup Files ===")
listOf(tempFile, tempDir, watchDir, lockFile, file1, file2, file3, testFile2)
.forEach { file ->
if (file.exists()) {
if (file.isDirectory) {
file.deleteRecursively()
} else {
file.delete()
}
println("Deleted: ${file.name}")
}
}
}Practical Application Examples
Log File Management System
kotlin
import java.io.File
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.withLock
class LogManager(
private val logDirectory: String = "logs",
private val maxFileSize: Long = 10 * 1024 * 1024, // 10MB
private val maxFiles: Int = 10
) {
private val lock = ReentrantLock()
private val dateFormat = SimpleDateFormat("yyyy-MM-dd")
private val timeFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS")
init {
File(logDirectory).mkdirs()
}
enum class LogLevel { DEBUG, INFO, WARN, ERROR }
fun log(level: LogLevel, message: String, throwable: Throwable? = null) {
lock.withLock {
val logFile = getCurrentLogFile()
val timestamp = timeFormat.format(Date())
val logEntry = buildString {
append("[$timestamp] [${level.name}] $message")
throwable?.let {
append("\n")
append(it.stackTraceToString())
}
append("\n")
}
logFile.appendText(logEntry)
// Check file size, rotate if necessary
if (logFile.length() > maxFileSize) {
rotateLogFiles()
}
}
}
private fun getCurrentLogFile(): File {
val today = dateFormat.format(Date())
return File(logDirectory, "app-$today.log")
}
private fun rotateLogFiles() {
val logFiles = File(logDirectory)
.listFiles { file -> file.name.matches(Regex("app-\\d{4}-\\d{2}-\\d{2}\\.log")) }
?.sortedByDescending { it.lastModified() }
?: return
// Delete old files exceeding limit
if (logFiles.size >= maxFiles) {
logFiles.drop(maxFiles - 1).forEach { file ->
file.delete()
println("Deleted old log file: ${file.name}")
}
}
}
fun getLogFiles(): List<File> {
return File(logDirectory)
.listFiles { file -> file.name.endsWith(".log") }
?.sortedByDescending { it.lastModified() }
?: emptyList()
}
fun searchLogs(keyword: String, level: LogLevel? = null): List<String> {
val results = mutableListOf<String>()
getLogFiles().forEach { file ->
file.useLines { lines ->
lines.forEach { line ->
val matchesKeyword = line.contains(keyword, ignoreCase = true)
val matchesLevel = level?.let { line.contains("[${it.name}]") } ?: true
if (matchesKeyword && matchesLevel) {
results.add("${file.name}: $line")
}
}
}
}
return results
}
fun getLogStatistics(): Map<String, Any> {
val stats = mutableMapOf<String, Any>()
val levelCounts = mutableMapOf<LogLevel, Int>()
var totalLines = 0
var totalSize = 0L
getLogFiles().forEach { file ->
totalSize += file.length()
file.useLines { lines ->
lines.forEach { line ->
totalLines++
LogLevel.values().forEach { level ->
if (line.contains("[${level.name}]")) {
levelCounts[level] = levelCounts.getOrDefault(level, 0) + 1
}
}
}
}
}
stats["totalFiles"] = getLogFiles().size
stats["totalLines"] = totalLines
stats["totalSize"] = totalSize
stats["levelCounts"] = levelCounts
return stats
}
fun cleanup() {
File(logDirectory).deleteRecursively()
}
}
// Config file manager
class ConfigManager(private val configFile: String = "app.properties") {
private val properties = mutableMapOf<String, String>()
init {
loadConfig()
}
private fun loadConfig() {
val file = File(configFile)
if (file.exists()) {
file.readLines().forEach { line ->
if (line.contains("=") && !line.startsWith("#")) {
val parts = line.split("=", limit = 2)
if (parts.size == 2) {
properties[parts[0].trim()] = parts[1].trim()
}
}
}
}
}
fun get(key: String, defaultValue: String = ""): String {
return properties[key] ?: defaultValue
}
fun getInt(key: String, defaultValue: Int = 0): Int {
return properties[key]?.toIntOrNull() ?: defaultValue
}
fun getBoolean(key: String, defaultValue: Boolean = false): Boolean {
return properties[key]?.toBoolean() ?: defaultValue
}
fun set(key: String, value: String) {
properties[key] = value
}
fun save() {
val content = buildString {
appendLine("# Application configuration file")
appendLine("# Generated: ${Date()}")
appendLine()
properties.forEach { (key, value) ->
appendLine("$key=$value")
}
}
File(configFile).writeText(content)
}
fun reload() {
properties.clear()
loadConfig()
}
}
fun main() {
println("=== Log File Management System Example ===")
// 1. Log manager test
val logManager = LogManager()
// Log different levels
logManager.log(LogLevel.INFO, "Application started")
logManager.log(LogLevel.DEBUG, "Debug info: User ID = 12345")
logManager.log(LogLevel.WARN, "Warning: High memory usage")
logManager.log(LogLevel.ERROR, "Error: Database connection failed", RuntimeException("Connection timeout"))
// Simulate bulk logging to trigger rotation
repeat(100) { i ->
logManager.log(LogLevel.INFO, "Batch log message #$i")
}
println("Logging complete")
// View log files
println("\nCurrent log files:")
logManager.getLogFiles().forEach { file ->
println("- ${file.name}: ${file.length()} bytes")
}
// Search logs
println("\nSearch logs containing 'Error':")
val errorLogs = logManager.searchLogs("Error")
errorLogs.take(3).forEach { println(" $it") }
// Log statistics
println("\nLog statistics:")
val stats = logManager.getLogStatistics()
stats.forEach { (key, value) ->
println(" $key: $value")
}
// 2. Config manager test
println("\n=== Config Manager Test ===")
val configManager = ConfigManager()
// Set config
configManager.set("app.name", "Kotlin File Handling Example")
configManager.set("app.version", "1.0.0")
configManager.set("debug.enabled", "true")
configManager.set("max.connections", "100")
// Save config
configManager.save()
println("Config saved")
// Read config
println("App name: ${configManager.get("app.name")}")
println("Version: ${configManager.get("app.version")}")
println("Debug mode: ${configManager.getBoolean("debug.enabled")}")
println("Max connections: ${configManager.getInt("max.connections")}")
// 3. File backup example
println("\n=== File Backup Example ===")
fun backupFile(sourceFile: File, backupDir: File = File("backups")): File? {
if (!sourceFile.exists()) return null
backupDir.mkdirs()
val timestamp = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
val backupName = "${sourceFile.nameWithoutExtension}_$timestamp.${sourceFile.extension}"
val backupFile = File(backupDir, backupName)
return try {
sourceFile.copyTo(backupFile)
println("Backup created: ${backupFile.name}")
backupFile
} catch (e: Exception) {
println("Backup failed: ${e.message}")
null
}
}
// Backup config file
val configFile = File("app.properties")
if (configFile.exists()) {
backupFile(configFile)
}
// Cleanup
println("\n=== Cleanup Resources ===")
logManager.cleanup()
File("app.properties").delete()
File("backups").deleteRecursively()
println("Cleanup complete")
}Best Practices
1. Exception Handling and Resource Management
kotlin
import java.io.*
// Safe file operations
class SafeFileOperations {
fun safeReadFile(filename: String): Result<String> {
return try {
val content = File(filename).readText()
Result.success(content)
} catch (e: FileNotFoundException) {
Result.failure(Exception("File not found: $filename", e))
} catch (e: IOException) {
Result.failure(Exception("Failed to read file: ${e.message}", e))
} catch (e: Exception) {
Result.failure(Exception("Unknown error: ${e.message}", e))
}
}
fun safeWriteFile(filename: String, content: String): Result<Unit> {
return try {
File(filename).writeText(content)
Result.success(Unit)
} catch (e: IOException) {
Result.failure(Exception("Failed to write file: ${e.message}", e))
} catch (e: Exception) {
Result.failure(Exception("Unknown error: ${e.message}", e))
}
}
// Use 'use' to ensure resources are properly closed
fun copyFileWithStreams(source: File, target: File): Result<Unit> {
return try {
FileInputStream(source).use { input ->
FileOutputStream(target).use { output ->
input.copyTo(output)
}
}
Result.success(Unit)
} catch (e: Exception) {
Result.failure(e)
}
}
}2. Performance Optimization
kotlin
import java.io.File
import java.nio.file.Files
import java.nio.file.Paths
class FilePerformanceOptimization {
// For large files, use sequence instead of reading all lines at once
fun processLargeFile(filename: String, processor: (String) -> Unit) {
File(filename).useLines { lines ->
lines.forEach(processor)
}
}
// Batch file operations
fun batchProcessFiles(directory: File, batchSize: Int = 100) {
directory.walkTopDown()
.filter { it.isFile }
.chunked(batchSize)
.forEach { batch ->
// Process files in batch
batch.forEach { file ->
// Process single file
}
}
}
// Use NIO for efficient file operations
fun efficientFileCopy(source: String, target: String) {
Files.copy(Paths.get(source), Paths.get(target))
}
}3. Cross-Platform Compatibility
kotlin
import java.io.File
class CrossPlatformFileOperations {
// Use File.separator to ensure correct path separator
fun createPath(vararg parts: String): String {
return parts.joinToString(File.separator)
}
// Normalize path
fun normalizePath(path: String): String {
return File(path).canonicalPath
}
// Check if filename is valid
fun isValidFilename(filename: String): Boolean {
val invalidChars = when {
System.getProperty("os.name").lowercase().contains("windows") ->
charArrayOf('<', '>', ':', '"', '|', '?', '*')
else -> charArrayOf('/')
}
return filename.none { it in invalidChars }
}
}Next Steps
After mastering file handling, let's learn about inheritance and polymorphism concepts in Kotlin.
Next Chapter: Inheritance and Polymorphism
Exercises
- Create a file synchronization tool that compares two directories and syncs differences
- Implement a simple text editor that supports file open, edit, and save operations
- Write a log analysis tool that counts different log levels and trends
- Design a file compression tool that supports multiple compression formats
- Create a configuration file management system that supports multiple formats (JSON, XML, Properties, etc.)