Android Kotlin 文件操作示例

Android Kotlin 文件操作示例,包括文本文件读写、文件复制移动、目录遍历和文件验证

💻 文本文件读写 kotlin

🟢 simple ⭐⭐

使用各种编码选项和错误处理读取和写入文本文件

⏱️ 20 min 🏷️ kotlin, android, file operations, io
Prerequisites: Basic Kotlin knowledge, Android SDK
// Android Kotlin Text File Read/Write Examples
// Using Android Context and Java IO

import android.content.Context
import java.io.*

// 1. Basic Text File Writing
class TextFileWriter(private val context: Context) {

    // Write simple text to internal storage
    fun writeText(filename: String, text: String) {
        try {
            context.openFileOutput(filename, Context.MODE_PRIVATE).use { output ->
                output.write(text.toByteArray(Charsets.UTF_8))
            }
            println("Text written to: $filename")
        } catch (e: Exception) {
            println("Error writing file: ${e.message}")
        }
    }

    // Write text with specific encoding
    fun writeText(filename: String, text: String, charset: Charsets) {
        try {
            context.openFileOutput(filename, Context.MODE_PRIVATE).use { output ->
                output.write(text.toByteArray(charset))
            }
            println("Text written with ${charset.name()} to: $filename")
        } catch (e: Exception) {
            println("Error writing file: ${e.message}")
        }
    }

    // Append text to existing file
    fun appendText(filename: String, text: String) {
        try {
            context.openFileOutput(filename, Context.MODE_APPEND).use { output ->
                output.write(text.toByteArray(Charsets.UTF_8))
            }
            println("Text appended to: $filename")
        } catch (e: Exception) {
            println("Error appending: ${e.message}")
        }
    }

    // Write text with line endings
    fun writeLines(filename: String, lines: List<String>) {
        val content = lines.joinToString("\n")
        writeText(filename, content)
        println("${lines.size} lines written to: $filename")
    }

    // Write to external storage
    fun writeToExternalFile(file: File, text: String) {
        try {
            FileOutputStream(file).use { output ->
                output.write(text.toByteArray(Charsets.UTF_8))
            }
            println("Text written to external: ${file.absolutePath}")
        } catch (e: Exception) {
            println("Error writing to external: ${e.message}")
        }
    }

    // Write with BufferedWriter
    fun writeWithBuffer(file: File, text: String) {
        try {
            BufferedWriter(FileWriter(file)).use { writer ->
                writer.write(text)
            }
            println("Text written with buffer to: ${file.absolutePath}")
        } catch (e: Exception) {
            println("Error writing with buffer: ${e.message}")
        }
    }
}

// 2. Basic Text File Reading
class TextFileReader(private val context: Context) {

    // Read entire file as string
    fun readText(filename: String): String? {
        return try {
            val content = context.openFileInput(filename).bufferedReader().use { it.readText() }
            println("Read ${content.length} characters from: $filename")
            content
        } catch (e: Exception) {
            println("Error reading file: ${e.message}")
            null
        }
    }

    // Read with encoding detection
    fun readTextWithEncoding(filename: String): String? {
        val encodings = listOf(Charsets.UTF_8, Charsets.ISO_8859_1, Charsets.UTF_16)

        for (encoding in encodings) {
            try {
                val content = context.openFileInput(filename).bufferedReader(encoding).use { it.readText() }
                println("Read file with encoding: ${encoding.name()}")
                return content
            } catch (e: Exception) {
                // Try next encoding
            }
        }

        println("Could not read file with any supported encoding")
        return null
    }

    // Read line by line
    fun readLines(filename: String): List<String>? {
        return try {
            val lines = context.openFileInput(filename).bufferedReader().use { it.readLines() }
            println("Read ${lines.size} lines from: $filename")
            lines
        } catch (e: Exception) {
            println("Error reading lines: ${e.message}")
            null
        }
    }

    // Read from external storage
    fun readFromExternalFile(file: File): String? {
        return try {
            FileInputStream(file).bufferedReader().use { it.readText() }
        } catch (e: Exception) {
            println("Error reading from external: ${e.message}")
            null
        }
    }

    // Read with BufferedReader (for large files)
    fun readWithBuffer(file: File): String? {
        return try {
            BufferedReader(FileReader(file)).use { it.readText() }
        } catch (e: Exception) {
            println("Error reading with buffer: ${e.message}")
            null
        }
    }

    // Read specific range
    fun readRange(file: File, start: Long, length: Int): String? {
        return try {
            RandomAccessFile(file, "r").use { raf ->
                raf.seek(start)
                val bytes = ByteArray(length)
                raf.read(bytes)
                String(bytes, Charsets.UTF_8)
            }
        } catch (e: Exception) {
            println("Error reading range: ${e.message}")
            null
        }
    }
}

// 3. File Info Helper
class FileInfoHelper(private val context: Context) {

    fun getFileAttributes(filename: String): File? {
        val file = File(context.filesDir, filename)
        return if (file.exists()) file else null
    }

    fun printFileInfo(filename: String) {
        val file = getFileAttributes(filename)
        if (file == null) {
            println("No file info available for: $filename")
            return
        }

        println("\n--- File Info: $filename ---")
        println("Size: ${file.length()} bytes")
        println("Modified: ${Date(file.lastModified())}")
        println("Path: ${file.absolutePath}")
        println("Can read: ${file.canRead()}")
        println("Can write: ${file.canWrite()}")
    }
}

// 4. Advanced Text Operations
class AdvancedTextOperations(private val context: Context) {

    // Read file with line processing callback
    fun processLinesInFile(filename: String, processor: (String, Int) -> Unit) {
        val lines = TextFileReader(context).readLines(filename) ?: return
        lines.forEachIndexed { index, line ->
            processor(line, index)
        }
    }

    // Search for text in file
    fun searchInFile(filename: String, searchText: String): List<Pair<String, Int>> {
        val lines = TextFileReader(context).readLines(filename) ?: return emptyList()
        val results = mutableListOf<Pair<String, Int>>()

        lines.forEachIndexed { index, line ->
            if (line.contains(searchText, ignoreCase = true)) {
                results.add(Pair(line, index + 1))
            }
        }

        return results
    }

    // Replace text in file
    fun replaceInFile(filename: String, searchText: String, replaceWith: String): Boolean {
        val content = TextFileReader(context).readText(filename) ?: return false
        val newContent = content.replace(searchText, replaceWith)
        TextFileWriter(context).writeText(filename, newContent)
        println("Replaced occurrences of '$searchText' in: $filename")
        return true
    }

    // Count words in file
    fun countWordsInFile(filename: String): Int {
        val content = TextFileReader(context).readText(filename) ?: return 0
        val words = content.split(Regex("\\s+"))
        val nonEmptyWords = words.filter { it.isNotEmpty() }
        return nonEmptyWords.size
    }

    // Read and parse CSV
    fun readCSV(filename: String): List<List<String>> {
        val content = TextFileReader(context).readText(filename) ?: return emptyList()
        return content.split("\n")
            .map { line -> line.split(",") }
            .filter { it.isNotEmpty() }
    }

    // Write JSON to file
    fun writeJSON(filename: String, json: String) {
        TextFileWriter(context).writeText(filename, json)
        println("JSON written to: $filename")
    }

    // Read JSON from file
    fun readJSON(filename: String): String? {
        return TextFileReader(context).readText(filename)
    }
}

// 5. Safe File Operations with Error Handling
class SafeFileOperations(private val context: Context) {

    enum class FileError {
        FILE_NOT_FOUND, PERMISSION_DENIED, INVALID_ENCODING, WRITE_FAILED
    }

    // Safe read with comprehensive error handling
    fun safeRead(filename: String): Result<String> {
        val file = File(context.filesDir, filename)

        if (!file.exists()) {
            return Result.failure(Exception("File not found: $filename"))
        }

        if (!file.canRead()) {
            return Result.failure(Exception("Permission denied: $filename"))
        }

        return try {
            val content = context.openFileInput(filename).bufferedReader().use { it.readText() }
            Result.success(content)
        } catch (e: Exception) {
            Result.failure(e)
        }
    }

    // Safe write with backup
    fun safeWrite(filename: String, text: String, createBackup: Boolean = true): Result<Unit> {
        if (createBackup) {
            val file = File(context.filesDir, filename)
            if (file.exists()) {
                val backupFile = File(context.filesDir, "$filename.backup")
                file.copyTo(backupFile, overwrite = true)
                println("Backup created at: ${backupFile.absolutePath}")
            }
        }

        return try {
            TextFileWriter(context).writeText(filename, text)
            Result.success(Unit)
        } catch (e: Exception) {
            Result.failure(e)
        }
    }
}

// 6. Character Encoding Handler
class EncodingHandler(private val context: Context) {

    // Detect file encoding
    fun detectEncoding(filename: String): Charsets? {
        val encodings = listOf(Charsets.UTF_8, Charsets.UTF_16, Charsets.ISO_8859_1)

        for (encoding in encodings) {
            try {
                context.openFileInput(filename).bufferedReader(encoding).use { it.readText() }
                return encoding
            } catch (e: Exception) {
                // Try next encoding
            }
        }

        return null
    }

    // Convert file encoding
    fun convertEncoding(filename: String, targetEncoding: Charsets): Boolean {
        val currentEncoding = detectEncoding(filename) ?: return false
        val content = context.openFileInput(filename).bufferedReader(currentEncoding).use { it.readText() }

        context.openFileOutput(filename, Context.MODE_PRIVATE).use { output ->
            output.write(content.toByteArray(targetEncoding))
        }

        println("Converted encoding from ${currentEncoding.name()} to ${targetEncoding.name()}")
        return true
    }
}

// Main demonstration
fun demonstrateTextFileOperations(context: Context) {
    println("=== Android Kotlin Text File Read/Write Examples ===\n")

    val testFile = "test_text_file.txt"
    val testContent = """
        Hello, World!
        This is a test file.
        Created with Kotlin on Android.
        Line 4
        Line 5
    """.trimIndent()

    val writer = TextFileWriter(context)
    val reader = TextFileReader(context)

    // 1. Basic write
    println("--- 1. Basic Write ---")
    writer.writeText(testFile, testContent)

    // 2. Basic read
    println("\n--- 2. Basic Read ---")
    val content = reader.readText(testFile)
    println("Content:\n$content")

    // 3. Read lines
    println("\n--- 3. Read Lines ---")
    val lines = reader.readLines(testFile)
    println("${lines?.size} lines:")
    lines?.forEachIndexed { index, line ->
        println("  ${index + 1}: $line")
    }

    // 4. Append text
    println("\n--- 4. Append Text ---")
    writer.appendText(testFile, "Appended line!")
    val updatedContent = reader.readText(testFile)
    println("Updated content: $updatedContent")

    // 5. File info
    println("\n--- 5. File Info ---")
    val fileInfo = FileInfoHelper(context)
    fileInfo.printFileInfo(testFile)

    // 6. Word count
    println("\n--- 6. Word Count ---")
    val advancedOps = AdvancedTextOperations(context)
    val wordCount = advancedOps.countWordsInFile(testFile)
    println("Total words: $wordCount")

    // 7. Search in file
    println("\n--- 7. Search in File ---")
    val results = advancedOps.searchInFile(testFile, "Kotlin")
    println("Found ${results.size} occurrences of 'Kotlin':")
    results.forEach { (line, lineNumber) ->
        println("  Line $lineNumber: $line")
    }

    println("\n=== All Text File Operations Completed ===")
}

💻 文件验证 kotlin

🟢 simple ⭐⭐⭐

检查文件是否存在,获取文件信息,验证文件类型,并比较文件

⏱️ 20 min 🏷️ kotlin, android, file operations, validation
Prerequisites: Basic Kotlin knowledge, Android SDK
// Android Kotlin File Validation Examples
// Using Android Context and Java IO

import android.content.Context
import java.io.*
import java.security.MessageDigest

// 1. Basic File Existence Check
class FileChecker(private val context: Context) {

    // Check if file exists
    fun fileExists(file: File): Boolean {
        return file.exists()
    }

    // Check if path is a file (not directory)
    fun isFile(file: File): Boolean {
        return file.isFile
    }

    // Check if path is a directory
    fun isDirectory(file: File): Boolean {
        return file.isDirectory
    }

    // Check if file is readable
    fun isReadable(file: File): Boolean {
        return file.canRead()
    }

    // Check if file is writable
    fun isWritable(file: File): Boolean {
        return file.canWrite()
    }

    // Comprehensive file check
    fun checkFile(file: File): FileStatus {
        if (!file.exists()) {
            return FileStatus(
                status = FileStatusType.NOT_FOUND,
                isReadable = false,
                isWritable = false,
                isExecutable = false
            )
        }

        return FileStatus(
            status = if (file.isDirectory) FileStatusType.DIRECTORY else FileStatusType.FILE,
            isReadable = file.canRead(),
            isWritable = file.canWrite(),
            isExecutable = file.canExecute()
        )
    }
}

// File Status Types
enum class FileStatusType {
    NOT_FOUND, FILE, DIRECTORY, SYMBOLIC_LINK
}

data class FileStatus(
    val status: FileStatusType,
    val isReadable: Boolean,
    val isWritable: Boolean,
    val isExecutable: Boolean
)

// 2. File Information
class FileInfo(private val context: Context) {

    // Get file size
    fun getSize(file: File): Long? {
        return if (file.exists()) file.length() else null
    }

    // Get creation date (API 26+)
    fun getCreationDate(file: File): Date? {
        return try {
            Date(java.nio.file.Files.getAttribute(
                file.toPath(),
                "creationTime"
            )?.toString()?.toLong() ?: 0L)
        } catch (e: Exception) {
            null
        }
    }

    // Get modification date
    fun getModificationDate(file: File): Date? {
        return if (file.exists()) Date(file.lastModified()) else null
    }

    // Get file extension
    fun getExtension(file: File): String {
        return file.extension
    }

    // Get file name without extension
    fun getNameWithoutExtension(file: File): String {
        return file.nameWithoutExtension
    }

    // Get MIME type
    fun getMIMEType(file: File): String? {
        return try {
            android.webkit.MimeTypeMap.getSingleton()
                .getMimeTypeFromExtension(file.extension)
        } catch (e: Exception) {
            null
        }
    }

    // Get detailed file info
    fun getDetailedInfo(file: File): DetailedFileInfo? {
        if (!file.exists()) return null

        return DetailedFileInfo(
            path = file.absolutePath,
            size = file.length(),
            creationDate = getCreationDate(file),
            modificationDate = getModificationDate(file),
            isHidden = file.isHidden,
            canRead = file.canRead(),
            canWrite = file.canWrite(),
            canExecute = file.canExecute()
        )
    }
}

// Detailed File Info Structure
data class DetailedFileInfo(
    val path: String,
    val size: Long,
    val creationDate: Date?,
    val modificationDate: Date?,
    val isHidden: Boolean,
    val canRead: Boolean,
    val canWrite: Boolean,
    val canExecute: Boolean
) {
    fun printInfo() {
        println("\n--- File Information ---")
        println("Path: $path")
        println("Size: ${formatSize(size)}")

        creationDate?.let {
            println("Created: ${formatDate(it)}")
        }

        modificationDate?.let {
            println("Modified: ${formatDate(it)}")
        }

        println("Hidden: $isHidden")
        println("Readable: $canRead")
        println("Writable: $canWrite")
        println("Executable: $canExecute")
    }

    private fun formatSize(bytes: Long): String {
        val kb = bytes / 1024.0
        val mb = kb / 1024
        val gb = mb / 1024

        return when {
            gb >= 1 -> "%.2f GB".format(gb)
            mb >= 1 -> "%.2f MB".format(mb)
            kb >= 1 -> "%.2f KB".format(kb)
            else -> "$bytes bytes"
        }
    }

    private fun formatDate(date: Date): String {
        val formatter = java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
        return formatter.format(date)
    }
}

// 3. File Type Validation
class FileTypeValidator(private val context: Context) {

    // Validate by extension
    fun isValidExtension(file: File, validExtensions: List<String>): Boolean {
        return validExtensions.contains(file.extension.lowercase())
    }

    // Validate image file
    fun isImageFile(file: File): Boolean {
        val imageExtensions = listOf("jpg", "jpeg", "png", "gif", "bmp", "webp")
        return isValidExtension(file, imageExtensions)
    }

    // Validate text file
    fun isTextFile(file: File): Boolean {
        val textExtensions = listOf("txt", "md", "html", "css", "js", "json", "xml", "csv")
        return isValidExtension(file, textExtensions)
    }

    // Validate by MIME type
    fun hasMIMEType(file: File, expectedMIME: String): Boolean {
        val mime = FileInfo(context).getMIMEType(file)
        return mime?.equals(expectedMIME, ignoreCase = true) ?: false
    }

    // Check if file is empty
    fun isEmpty(file: File): Boolean {
        return file.exists() && file.length() == 0L
    }

    // Validate file size constraints
    fun isSizeValid(file: File, minSize: Long, maxSize: Long): Boolean {
        val size = file.length()
        return size in minSize..maxSize
    }
}

// 4. File Comparison
class FileComparator(private val context: Context) {

    // Compare files by size
    fun compareSize(file1: File, file2: File): ComparisonResult {
        val size1 = file1.length()
        val size2 = file2.length()

        return when {
            size1 < size2 -> ComparisonResult.LESS_THAN
            size1 > size2 -> ComparisonResult.GREATER_THAN
            else -> ComparisonResult.EQUAL
        }
    }

    // Compare files by content
    fun compareContent(file1: File, file2: File): Boolean {
        return try {
            file1.readBytes().contentEquals(file2.readBytes())
        } catch (e: Exception) {
            false
        }
    }

    // Compare files by modification date
    fun compareModificationDate(file1: File, file2: File): ComparisonResult {
        val date1 = file1.lastModified()
        val date2 = file2.lastModified()

        return when {
            date1 < date2 -> ComparisonResult.LESS_THAN
            date1 > date2 -> ComparisonResult.GREATER_THAN
            else -> ComparisonResult.EQUAL
        }
    }

    // Check if files are identical (size and content)
    fun areIdentical(file1: File, file2: File): Boolean {
        return compareSize(file1, file2) == ComparisonResult.EQUAL &&
               compareContent(file1, file2)
    }

    // Compare using checksum
    fun compareChecksum(file1: File, file2: File): Boolean {
        val hash1 = calculateChecksum(file1)
        val hash2 = calculateChecksum(file2)

        return hash1 != null && hash2 != null && hash1 == hash2
    }

    // Calculate file checksum (SHA256)
    fun calculateChecksum(file: File): String? {
        return try {
            val digest = MessageDigest.getInstance("SHA-256")
            file.inputStream().use { input ->
                val buffer = ByteArray(8192)
                var bytesRead: Int

                while (input.read(buffer).also { bytesRead = it } != -1) {
                    digest.update(buffer, 0, bytesRead)
                }
            }

            val hash = digest.digest()
            hash.joinToString("") { "%02x".format(it) }
        } catch (e: Exception) {
            null
        }
    }
}

// Comparison Result Enum
enum class ComparisonResult {
    LESS_THAN, EQUAL, GREATER_THAN
}

// 5. Path Validation
class PathValidator(private val context: Context) {

    // Validate path format
    fun isValidPath(file: File): Boolean {
        return try {
            file.canonicalPath.isNotEmpty()
        } catch (e: Exception) {
            false
        }
    }

    // Get absolute path from relative
    fun getAbsolutePath(file: File): String {
        return file.absolutePath
    }

    // Validate file name
    fun isValidFileName(name: String): Boolean {
        // Check for invalid characters
        val invalidChars = setOf('/', '\\', ':', '*', '?', '"', '<', '>', '|')
        return !name.isEmpty() && !name.any { it in invalidChars }
    }

    // Sanitize file name
    fun sanitizeFileName(name: String): String {
        val invalidChars = setOf('/', '\\', ':', '*', '?', '"', '<', '>', '|')
        return name.map { if (it in invalidChars) '_' else it }.joinToString("")
    }
}

// 6. File Integrity Checker
class FileIntegrityChecker(private val context: Context) {

    // Verify file hasn't been corrupted using stored hash
    fun verifyIntegrity(file: File, expectedHash: String): Boolean {
        val actualHash = FileComparator(context).calculateChecksum(file)
        return actualHash?.equals(expectedHash, ignoreCase = true) ?: false
    }

    // Generate hash file for integrity checking
    fun generateHashFile(file: File): String? {
        return FileComparator(context).calculateChecksum(file)
    }

    // Quick check (just size)
    fun quickCheck(file: File, expectedSize: Long): Boolean {
        return file.exists() && file.length() == expectedSize
    }
}

// Main demonstration
fun demonstrateFileValidation(context: Context) {
    println("=== Android Kotlin File Validation Examples ===\n")

    val testFile = File(context.cacheDir, "validation_test.txt")
    val testDir = File(context.cacheDir, "validation_test_dir")

    // Create test file
    testFile.writeText("Test content for validation")
    testDir.mkdirs()

    val checker = FileChecker(context)

    // 1. File existence checks
    println("--- 1. File Existence Checks ---")
    println("File exists: ${checker.fileExists(testFile)}")
    println("Is file: ${checker.isFile(testFile)}")
    println("Is directory: ${checker.isDirectory(testDir)}")
    println("Is readable: ${checker.isReadable(testFile)}")
    println("Is writable: ${checker.isWritable(testFile)}")

    // 2. Comprehensive file check
    println("\n--- 2. Comprehensive File Check ---")
    val status = checker.checkFile(testFile)
    println("Status: ${status.status}")
    println("Readable: ${status.isReadable}")
    println("Writable: ${status.isWritable}")
    println("Executable: ${status.isExecutable}")

    // 3. File information
    println("\n--- 3. File Information ---")
    val fileInfo = FileInfo(context)
    val size = fileInfo.getSize(testFile)
    println("Size: $size bytes")

    val modDate = fileInfo.getModificationDate(testFile)
    println("Modified: $modDate")

    println("Extension: ${fileInfo.getExtension(testFile)}")
    println("Name without ext: ${fileInfo.getNameWithoutExtension(testFile)}")

    // 4. Detailed info
    println("\n--- 4. Detailed File Info ---")
    val info = fileInfo.getDetailedInfo(testFile)
    info?.printInfo()

    // 5. File type validation
    println("\n--- 5. File Type Validation ---")
    val validator = FileTypeValidator(context)
    println("Is text file: ${validator.isTextFile(testFile)}")
    println("Is image file: ${validator.isImageFile(testFile)}")
    println("Is empty: ${validator.isEmpty(testFile)}")
    println("Size valid (1-1000 bytes): ${validator.isSizeValid(testFile, 1, 1000)}")

    // 6. File comparison
    println("\n--- 6. File Comparison ---")
    val testFile2 = File(context.cacheDir, "validation_test2.txt")
    testFile2.writeText("Different content")

    val comparator = FileComparator(context)
    val sizeCompare = comparator.compareSize(testFile, testFile2)
    println("Size comparison: $sizeCompare")

    val contentEqual = comparator.compareContent(testFile, testFile)
    println("Content equal: $contentEqual")

    // 7. Checksum
    println("\n--- 7. Checksum ---")
    val checksum = comparator.calculateChecksum(testFile)
    println("SHA256: $checksum")

    val integrityChecker = FileIntegrityChecker(context)
    checksum?.let {
        val valid = integrityChecker.verifyIntegrity(testFile, it)
        println("Integrity check: ${if (valid) "PASSED" else "FAILED"}")
    }

    // 8. Path validation
    println("\n--- 8. Path Validation ---")
    val pathValidator = PathValidator(context)
    println("Is valid path: ${pathValidator.isValidPath(testFile)}")
    println("Valid filename 'test.txt': ${pathValidator.isValidFileName("test.txt")}")
    println("Valid filename 'test?.txt': ${pathValidator.isValidFileName("test?.txt")}")
    println("Sanitized name: ${pathValidator.sanitizeFileName("test?.txt")}")

    // Cleanup
    testFile.delete()
    testFile2.delete()
    testDir.deleteRecursively()
    println("\nCleanup completed")

    println("\n=== All File Validation Examples Completed ===")
}

💻 文件复制/移动 kotlin

🟡 intermediate ⭐⭐⭐

使用覆盖选项、进度跟踪和错误恢复复制和移动文件

⏱️ 25 min 🏷️ kotlin, android, file operations
Prerequisites: Basic Kotlin knowledge, Android SDK
// Android Kotlin File Copy/Move Examples
// Using Android Context and Java IO

import android.content.Context
import java.io.*

// 1. Basic File Copy
class FileCopier(private val context: Context) {

    // Simple copy using streams
    fun copyFile(source: File, destination: File) {
        try {
            FileInputStream(source).use { input ->
                FileOutputStream(destination).use { output ->
                    input.copyTo(output)
                }
            }
            println("Copied: ${source.absolutePath} -> ${destination.absolutePath}")
        } catch (e: Exception) {
            println("Error copying file: ${e.message}")
        }
    }

    // Copy with overwrite option
    fun copyFile(source: File, destination: File, overwrite: Boolean) {
        if (destination.exists()) {
            if (overwrite) {
                destination.delete()
                println("Removed existing file: ${destination.absolutePath}")
            } else {
                println("File already exists: ${destination.absolutePath}")
                return
            }
        }

        copyFile(source, destination)
    }

    // Copy to directory
    fun copyFileToDirectory(source: File, directory: File) {
        if (!directory.exists()) {
            directory.mkdirs()
        }

        val destination = File(directory, source.name)
        copyFile(source, destination)
    }

    // Copy with progress callback
    fun copyFileWithProgress(source: File, destination: File,
                            progress: ((Double) -> Unit)?) {
        try {
            if (!source.exists()) {
                println("Source file not found: ${source.absolutePath}")
                return
            }

            val fileSize = source.length()
            FileInputStream(source).use { input ->
                FileOutputStream(destination).use { output ->
                    val buffer = ByteArray(1024 * 1024) // 1MB buffer
                    var bytesCopied = 0L
                    var bytesRead: Int

                    while (input.read(buffer).also { bytesRead = it } != -1) {
                        output.write(buffer, 0, bytesRead)
                        bytesCopied += bytesRead

                        val percent = bytesCopied.toDouble() / fileSize
                        progress?.invoke(percent)
                    }
                }
            }

            println("Copy completed: ${destination.absolutePath}")
        } catch (e: Exception) {
            println("Error copying with progress: ${e.message}")
        }
    }

    // Copy using NIO (Android API 26+)
    fun copyFileNIO(source: File, destination: File) {
        try {
            java.nio.file.Files.copy(
                source.toPath(),
                destination.toPath(),
                java.nio.file.StandardCopyOption.REPLACE_EXISTING
            )
            println("Copied using NIO: ${source.absolutePath} -> ${destination.absolutePath}")
        } catch (e: Exception) {
            println("Error copying with NIO: ${e.message}")
        }
    }
}

// 2. Basic File Move
class FileMover(private val context: Context) {

    // Simple move
    fun moveFile(source: File, destination: File): Boolean {
        val result = source.renameTo(destination)
        if (result) {
            println("Moved: ${source.absolutePath} -> ${destination.absolutePath}")
        } else {
            println("Failed to move: ${source.absolutePath}")
        }
        return result
    }

    // Move with overwrite
    fun moveFile(source: File, destination: File, overwrite: Boolean): Boolean {
        if (destination.exists()) {
            if (overwrite) {
                destination.delete()
                println("Removed existing file: ${destination.absolutePath}")
            } else {
                println("File already exists: ${destination.absolutePath}")
                return false
            }
        }

        return moveFile(source, destination)
    }

    // Move to directory
    fun moveFileToDirectory(source: File, directory: File): Boolean {
        if (!directory.exists()) {
            directory.mkdirs()
        }

        val destination = File(directory, source.name)
        return moveFile(source, destination)
    }

    // Safe move with backup
    fun moveFileWithBackup(source: File, destination: File): Boolean {
        // Create backup if destination exists
        if (destination.exists()) {
            val backupFile = File(destination.parent, "${destination.name}.backup")
            destination.renameTo(backupFile)
            println("Backup created: ${backupFile.absolutePath}")
        }

        return moveFile(source, destination)
    }

    // Move using copy and delete (for cross-device moves)
    fun moveFileCopyDelete(source: File, destination: File): Boolean {
        return try {
            // Copy first
            FileInputStream(source).use { input ->
                FileOutputStream(destination).use { output ->
                    input.copyTo(output)
                }
            }

            // Delete source after successful copy
            source.delete()
            println("Moved (copy+delete): ${source.absolutePath} -> ${destination.absolutePath}")
            true
        } catch (e: Exception) {
            println("Error moving with copy+delete: ${e.message}")
            false
        }
    }
}

// 3. Batch Operations
class BatchFileOperations(private val context: Context) {

    // Copy multiple files
    fun copyFiles(sources: List<File>, destinationDirectory: File) {
        if (!destinationDirectory.exists()) {
            destinationDirectory.mkdirs()
        }

        var successCount = 0
        var failureCount = 0

        val copier = FileCopier(context)
        for (source in sources) {
            try {
                val destination = File(destinationDirectory, source.name)
                copier.copyFile(source, destination, overwrite = true)
                successCount++
            } catch (e: Exception) {
                println("Failed to copy ${source.name}: ${e.message}")
                failureCount++
            }
        }

        println("Batch copy completed: $successCount succeeded, $failureCount failed")
    }

    // Move multiple files
    fun moveFiles(sources: List<File>, destinationDirectory: File) {
        if (!destinationDirectory.exists()) {
            destinationDirectory.mkdirs()
        }

        var successCount = 0
        var failureCount = 0

        val mover = FileMover(context)
        for (source in sources) {
            try {
                val destination = File(destinationDirectory, source.name)
                mover.moveFile(source, destination, overwrite = true)
                successCount++
            } catch (e: Exception) {
                println("Failed to move ${source.name}: ${e.message}")
                failureCount++
            }
        }

        println("Batch move completed: $successCount succeeded, $failureCount failed")
    }

    // Copy directory recursively
    fun copyDirectory(sourceDir: File, destinationDir: File) {
        if (!sourceDir.isDirectory) {
            println("Not a directory: ${sourceDir.absolutePath}")
            return
        }

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

        val files = sourceDir.listFiles() ?: emptyArray()

        for (file in files) {
            val destination = File(destinationDir, file.name)
            if (file.isDirectory) {
                copyDirectory(file, destination)
            } else {
                FileCopier(context).copyFile(file, destination)
            }
        }

        println("Directory copied: ${sourceDir.absolutePath} -> ${destinationDir.absolutePath}")
    }

    // Move directory
    fun moveDirectory(sourceDir: File, destinationDir: File): Boolean {
        if (!sourceDir.isDirectory) {
            println("Not a directory: ${sourceDir.absolutePath}")
            return false
        }

        val result = sourceDir.renameTo(destinationDir)
        if (result) {
            println("Directory moved: ${sourceDir.absolutePath} -> ${destinationDir.absolutePath}")
        } else {
            // Try recursive move
            copyDirectory(sourceDir, destinationDir)
            sourceDir.deleteRecursively()
        }

        return result
    }
}

// 4. Smart File Operations
class SmartFileOperations(private val context: Context) {

    // Copy with duplicate handling
    fun smartCopy(source: File, destination: File) {
        if (destination.exists()) {
            // Add number suffix
            val baseName = destination.nameWithoutExtension
            val extension = destination.extension
            var counter = 1
            var newPath = destination

            while (newPath.exists()) {
                val newBaseName = "${baseName}_$counter"
                newPath = File(destination.parent, "$newBaseName.$extension")
                counter++
            }

            println("Destination exists, using: ${newPath.absolutePath}")
            FileCopier(context).copyFile(source, newPath)
        } else {
            FileCopier(context).copyFile(source, destination)
        }
    }

    // Safe move with undo
    fun moveWithUndo(source: File, destination: File): (() -> Unit)? {
        val originalSource = File(source.absolutePath)

        if (destination.exists()) {
            destination.delete()
        }

        val success = FileMover(context).moveFile(source, destination)

        if (success) {
            // Return undo function
            return {
                FileMover(context).moveFile(destination, originalSource)
                println("Undo: Moved back to ${originalSource.absolutePath}")
            }
        }

        return null
    }

    // Copy with file verification
    fun verifiedCopy(source: File, destination: File): Boolean {
        val sourceSize = source.length()

        FileCopier(context).copyFile(source, destination, overwrite = true)

        // Verify by comparing file sizes
        val destSize = destination.length()

        if (sourceSize == destSize) {
            println("Verified copy: $sourceSize bytes")
            return true
        } else {
            println("Verification failed: size mismatch")
            return false
        }
    }
}

// 5. File Operation with Attributes
class FileOperationWithAttributes(private val context: Context) {

    // Copy preserving attributes (using NIO)
    fun copyWithAttributes(source: File, destination: File) {
        try {
            java.nio.file.Files.copy(
                source.toPath(),
                destination.toPath(),
                java.nio.file.StandardCopyOption.COPY_ATTRIBUTES,
                java.nio.file.StandardCopyOption.REPLACE_EXISTING
            )
            println("Copied with attributes preserved")
        } catch (e: Exception) {
            println("Error copying with attributes: ${e.message}")
        }
    }
}

// Main demonstration
fun demonstrateFileCopyMove(context: Context) {
    println("=== Android Kotlin File Copy/Move Examples ===\n")

    val tempDir = File(context.cacheDir, "file_operations_test")
    tempDir.mkdirs()

    val sourceFile = File(context.cacheDir, "source_test.txt")
    val destFile1 = File(context.cacheDir, "dest_test1.txt")
    val destFile2 = File(context.cacheDir, "dest_test2.txt")

    // Create test file
    sourceFile.writeText("Test content for copy/move")

    val copier = FileCopier(context)

    // 1. Simple copy
    println("--- 1. Simple Copy ---")
    copier.copyFile(sourceFile, destFile1)

    // 2. Copy with progress
    println("\n--- 2. Copy with Progress ---")
    copier.copyFileWithProgress(sourceFile, destFile2) { progress ->
        println("Progress: ${(progress * 100).toInt()}%")
    }

    // 3. Simple move
    println("\n--- 3. Simple Move ---")
    val moveSource = File(context.cacheDir, "move_source.txt")
    val moveDest = File(context.cacheDir, "move_dest.txt")
    moveSource.writeText("File to move")

    val mover = FileMover(context)
    mover.moveFile(moveSource, moveDest)

    // 4. Smart copy (handle duplicates)
    println("\n--- 4. Smart Copy ---")
    val smartSource = File(context.cacheDir, "smart_source.txt")
    val smartDest = File(context.cacheDir, "smart_dest.txt")
    smartSource.writeText("Smart copy test")

    val smartOps = SmartFileOperations(context)
    smartOps.smartCopy(smartSource, smartDest)
    smartOps.smartCopy(smartSource, smartDest) // Will create duplicate

    // 5. Batch copy
    println("\n--- 5. Batch Copy ---")
    val files = (1..3).map { File(context.cacheDir, "batch_source$it.txt") }
    files.forEach { it.writeText("Batch file ${it.nameWithoutExtension}") }

    val batchOps = BatchFileOperations(context)
    batchOps.copyFiles(files, tempDir)

    // 6. Verified copy
    println("\n--- 6. Verified Copy ---")
    val verifySource = File(context.cacheDir, "verify_source.txt")
    val verifyDest = File(context.cacheDir, "verify_dest.txt")
    verifySource.writeText("Verification test")

    smartOps.verifiedCopy(verifySource, verifyDest)

    // 7. Move with undo
    println("\n--- 7. Move with Undo ---")
    val undoSource = File(context.cacheDir, "undo_source.txt")
    val undoDest = File(context.cacheDir, "undo_dest.txt")
    undoSource.writeText("Undo test")

    val undo = smartOps.moveWithUndo(undoSource, undoDest)
    undo?.invoke()

    // Cleanup
    tempDir.deleteRecursively()
    println("\nCleanup completed")

    println("\n=== All File Copy/Move Examples Completed ===")
}

💻 目录遍历 kotlin

🟡 intermediate ⭐⭐⭐⭐

递归遍历目录,使用过滤器列出文件,并搜索特定文件类型

⏱️ 30 min 🏷️ kotlin, android, file operations, directory
Prerequisites: Intermediate Kotlin, Android SDK
// Android Kotlin Directory Traversal Examples
// Using Android Context and Java IO

import android.content.Context
import java.io.*

// 1. Basic Directory Listing
class DirectoryLister(private val context: Context) {

    // List all items in directory
    fun listDirectory(directory: File): List<String>? {
        if (!directory.exists() || !directory.isDirectory) {
            println("Not a directory: ${directory.absolutePath}")
            return null
        }

        return directory.listFiles()?.map { it.name }?.toList()
    }

    // List with details
    fun listDirectoryWithDetails(directory: File): List<FileItem>? {
        if (!directory.exists() || !directory.isDirectory) {
            return null
        }

        return directory.listFiles()?.map { file ->
            FileItem(
                name = file.name,
                path = file.absolutePath,
                isDirectory = file.isDirectory,
                size = if (file.isFile) file.length() else 0L,
                modifiedDate = Date(file.lastModified())
            )
        }?.toList()
    }

    // List only files
    fun listFiles(directory: File): List<File>? {
        return directory.listFiles()?.filter { it.isFile }?.toList()
    }

    // List only subdirectories
    fun listSubdirectories(directory: File): List<File>? {
        return directory.listFiles()?.filter { it.isDirectory }?.toList()
    }
}

// File Item Structure
data class FileItem(
    val name: String,
    val path: String,
    val isDirectory: Boolean,
    var size: Long = 0,
    var modifiedDate: Date? = null
)

// 2. Recursive Directory Traversal
class DirectoryTraverser(private val context: Context) {

    // Recursively find all files
    fun findAllFiles(directory: File): List<File> {
        val files = mutableListOf<File>()

        if (!directory.exists() || !directory.isDirectory) {
            return files
        }

        directory.listFiles()?.forEach { file ->
            if (file.isDirectory) {
                files.addAll(findAllFiles(file))
            } else {
                files.add(file)
            }
        }

        return files
    }

    // Recursive traversal with callback
    fun traverseDirectory(directory: File, callback: (File, Int) -> Unit) {
        traverseDirectory(directory, depth = 0, callback = callback)
    }

    private fun traverseDirectory(directory: File, depth: Int, callback: (File, Int) -> Unit) {
        if (!directory.exists() || !directory.isDirectory) {
            return
        }

        directory.listFiles()?.forEach { file ->
            callback(file, depth)

            if (file.isDirectory) {
                traverseDirectory(file, depth + 1, callback)
            }
        }
    }

    // Get directory tree
    fun getDirectoryTree(directory: File, maxDepth: Int = Int.MAX_VALUE): DirectoryNode {
        val root = DirectoryNode(
            name = directory.name,
            path = directory.absolutePath,
            isDirectory = true,
            depth = 0
        )
        buildTree(root, maxDepth)
        return root
    }

    private fun buildTree(node: DirectoryNode, maxDepth: Int) {
        if (node.depth >= maxDepth) return

        val directory = File(node.path)
        if (!directory.exists() || !directory.isDirectory) {
            return
        }

        directory.listFiles()?.sortedBy { it.name }?.forEach { file ->
            val childNode = DirectoryNode(
                name = file.name,
                path = file.absolutePath,
                isDirectory = file.isDirectory,
                depth = node.depth + 1
            )
            node.children.add(childNode)

            if (file.isDirectory) {
                buildTree(childNode, maxDepth)
            }
        }
    }
}

// Directory Tree Node
data class DirectoryNode(
    val name: String,
    val path: String,
    val isDirectory: Boolean,
    val depth: Int,
    var children: MutableList<DirectoryNode> = mutableListOf()
) {
    fun printTree() {
        val indent = "  ".repeat(depth)
        val prefix = if (isDirectory) "📁" else "📄"
        println("$prefix $indent$name")
        children.forEach { it.printTree() }
    }
}

// 3. File Filtering
class FileFilter(private val context: Context) {

    // Filter by extension
    fun filterByExtension(directory: File, extensions: List<String>): List<File> {
        val traverser = DirectoryTraverser(context)
        val files = traverser.findAllFiles(directory)

        return files.filter { file ->
            extensions.contains(file.extension.lowercase())
        }
    }

    // Filter by name pattern
    fun filterByNamePattern(directory: File, pattern: String): List<File> {
        val traverser = DirectoryTraverser(context)
        val files = traverser.findAllFiles(directory)
        val regex = pattern.toRegex()

        return files.filter { file ->
            regex.containsMatchIn(file.name)
        }
    }

    // Filter by size
    fun filterBySize(directory: File, minSize: Long, maxSize: Long): List<File> {
        val traverser = DirectoryTraverser(context)
        val files = traverser.findAllFiles(directory)

        return files.filter { file ->
            file.length() in minSize..maxSize
        }
    }

    // Filter by date
    fun filterByModifiedDate(directory: File, startDate: Date, endDate: Date): List<File> {
        val traverser = DirectoryTraverser(context)
        val files = traverser.findAllFiles(directory)

        return files.filter { file ->
            val modifiedDate = file.lastModified()
            modifiedDate >= startDate.time && modifiedDate <= endDate.time
        }
    }

    // Find duplicates by size
    fun findPotentialDuplicates(directory: File): List<List<File>> {
        val traverser = DirectoryTraverser(context)
        val files = traverser.findAllFiles(directory)

        val sizeGroups = files.groupBy { it.length() }

        // Return groups with more than one file
        return sizeGroups.values.filter { it.size > 1 }
    }
}

// 4. Directory Statistics
class DirectoryStatistics(private val context: Context) {

    data class Stats(
        var totalFiles: Int = 0,
        var totalDirectories: Int = 0,
        var totalSize: Long = 0,
        var largestFile: Pair<String, Long>? = null,
        val extensionCounts: MutableMap<String, Int> = mutableMapOf()
    )

    fun calculateStatistics(directory: File): Stats {
        val stats = Stats()
        val traverser = DirectoryTraverser(context)

        traverser.traverseDirectory(directory) { file, _ ->
            if (file.isDirectory) {
                stats.totalDirectories++
            } else {
                stats.totalFiles++
                val size = file.length()
                stats.totalSize += size

                // Track largest file
                if (stats.largestFile == null || size > stats.largestFile!!.second) {
                    stats.largestFile = Pair(file.absolutePath, size)
                }

                // Count extensions
                val ext = if (file.extension.isEmpty()) "(no extension)" else file.extension.lowercase()
                stats.extensionCounts[ext] = (stats.extensionCounts[ext] ?: 0) + 1
            }
        }

        return stats
    }

    fun printStatistics(stats: Stats) {
        println("\n=== Directory Statistics ===")
        println("Total files: ${stats.totalFiles}")
        println("Total directories: ${stats.totalDirectories}")
        println("Total size: ${formatBytes(stats.totalSize)}")

        stats.largestFile?.let { (path, size) ->
            println("Largest file: ${File(path).name}")
            println("  Size: ${formatBytes(size)}")
        }

        println("\nExtension counts:")
        stats.extensionCounts.toList()
            .sortedByDescending { it.second }
            .forEach { (ext, count) ->
                println("  .$ext: $count")
            }
    }

    private fun formatBytes(bytes: Long): String {
        val kb = bytes / 1024.0
        val mb = kb / 1024
        val gb = mb / 1024

        return when {
            gb >= 1 -> "%.2f GB".format(gb)
            mb >= 1 -> "%.2f MB".format(mb)
            kb >= 1 -> "%.2f KB".format(kb)
            else -> "$bytes bytes"
        }
    }
}

// 5. File Searcher
class FileSearcher(private val context: Context) {

    // Search files by name
    fun searchByName(directory: File, name: String): List<File> {
        val traverser = DirectoryTraverser(context)
        val files = traverser.findAllFiles(directory)

        return files.filter { file ->
            file.name.contains(name, ignoreCase = true)
        }
    }

    // Search files containing text
    fun searchByContent(directory: File, searchText: String): List<File> {
        val traverser = DirectoryTraverser(context)
        val files = traverser.findAllFiles(directory)

        return files.filter { file ->
            try {
                val content = file.readText()
                content.contains(searchText, ignoreCase = true)
            } catch (e: Exception) {
                false
            }
        }
    }

    // Find recently modified files
    fun findRecentlyModified(directory: File, withinMillis: Long): List<File> {
        val traverser = DirectoryTraverser(context)
        val files = traverser.findAllFiles(directory)
        val cutoffDate = System.currentTimeMillis() - withinMillis

        return files.filter { file ->
            file.lastModified() > cutoffDate
        }
    }
}

// Main demonstration
fun demonstrateDirectoryTraversal(context: Context) {
    println("=== Android Kotlin Directory Traversal Examples ===\n")

    val testDir = File(context.cacheDir, "directory_traversal_test")
    testDir.mkdirs()

    // Create some test files
    File(testDir, "file1.txt").writeText("File 1")
    File(testDir, "file2.md").writeText("File 2")

    // Create subdirectory
    val subDir = File(testDir, "subdir")
    subDir.mkdirs()
    File(subDir, "subfile1.txt").writeText("Subfile 1")

    val lister = DirectoryLister(context)

    // 1. List directory
    println("--- 1. List Directory ---")
    val items = lister.listDirectory(testDir)
    println("Items in ${testDir.name}:")
    items?.forEach { println("  - $it") }

    // 2. List with details
    println("\n--- 2. List with Details ---")
    val detailedItems = lister.listDirectoryWithDetails(testDir)
    println("Files in ${testDir.name}:")
    detailedItems?.forEach { item ->
        val type = if (item.isDirectory) "DIR " else "FILE"
        println("  [$type] ${item.name} - ${item.size} bytes")
    }

    // 3. Recursive traversal
    println("\n--- 3. Recursive Traversal ---")
    val traverser = DirectoryTraverser(context)
    val allFiles = traverser.findAllFiles(testDir)
    println("All files:")
    allFiles.forEach { println("  - ${it.absolutePath}") }

    // 4. Directory tree
    println("\n--- 4. Directory Tree ---")
    val tree = traverser.getDirectoryTree(testDir, maxDepth = 2)
    tree.printTree()

    // 5. Filter by extension
    println("\n--- 5. Filter by Extension ---")
    val fileFilter = FileFilter(context)
    val txtFiles = fileFilter.filterByExtension(testDir, listOf("txt"))
    println("Text files:")
    txtFiles.forEach { println("  - ${it.name}") }

    // 6. Directory statistics
    println("\n--- 6. Directory Statistics ---")
    val stats = DirectoryStatistics(context)
    val directoryStats = stats.calculateStatistics(testDir)
    stats.printStatistics(directoryStats)

    // 7. Search by name
    println("\n--- 7. Search by Name ---")
    val searcher = FileSearcher(context)
    val searchResults = searcher.searchByName(testDir, "file")
    println("Files containing 'file':")
    searchResults.forEach { println("  - ${it.name}") }

    // Cleanup
    testDir.deleteRecursively()
    println("\nCleanup completed")

    println("\n=== All Directory Traversal Examples Completed ===")
}