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 ===")
}