Skip to content

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

  1. Create a file synchronization tool that compares two directories and syncs differences
  2. Implement a simple text editor that supports file open, edit, and save operations
  3. Write a log analysis tool that counts different log levels and trends
  4. Design a file compression tool that supports multiple compression formats
  5. Create a configuration file management system that supports multiple formats (JSON, XML, Properties, etc.)

Content is for learning and research only.