(Kotlin/코틀린) 파일 입출력 — readText, writeText, forEachLine


1. 파일 읽기 — readText

파일 전체를 한 번에 문자열로 읽습니다.

import java.io.File

val file = File("data.txt")

// 전체 내용 읽기
val content = file.readText()
println(content)

// 인코딩 지정
val contentUtf8 = file.readText(Charsets.UTF_8)

// 줄 단위로 List<String> 반환
val lines = file.readLines()
lines.forEach { println(it) }

readText()는 파일을 메모리에 전부 올리므로 소용량 파일에 적합합니다.


2. 파일 쓰기 — writeText / appendText

val file = File("output.txt")

// 파일 생성 또는 덮어쓰기
file.writeText("Hello, Kotlin!\n")
file.writeText("두 번째 줄\n", Charsets.UTF_8)

// 파일에 내용 추가 (append)
file.appendText("추가된 줄\n")
// 줄 목록을 한 번에 쓰기
val lines = listOf("첫 번째", "두 번째", "세 번째")
File("list.txt").writeText(lines.joinToString("\n"))

3. forEachLine — 줄 단위 처리

파일을 한 줄씩 스트리밍으로 읽습니다. 대용량 파일에 적합합니다.

File("large.txt").forEachLine { line ->
    println(line)
}

// 인코딩 지정
File("large.txt").forEachLine(Charsets.UTF_8) { line ->
    if (line.startsWith("ERROR")) {
        println("에러 발견: $line")
    }
}

forEachLine은 내부적으로 BufferedReader를 사용하고 자동으로 닫습니다.


4. useLines — 시퀀스로 처리

Sequence<String>으로 줄을 처리합니다. 필터링, 변환 등 체이닝이 자연스럽습니다.

// 특정 조건 줄만 수집
val errorLines = File("app.log").useLines { lines ->
    lines
        .filter { it.contains("ERROR") }
        .map { it.trim() }
        .toList()
}

// 줄 수 세기
val lineCount = File("data.txt").useLines { it.count() }

// 첫 번째 매칭 줄 찾기
val firstError = File("app.log").useLines { lines ->
    lines.firstOrNull { it.startsWith("ERROR") }
}

useLines 블록이 끝나면 파일이 자동으로 닫힙니다.


5. bufferedReader / bufferedWriter

세밀한 제어가 필요할 때 사용합니다.

// 읽기
File("data.txt").bufferedReader().use { reader ->
    var line: String?
    while (reader.readLine().also { line = it } != null) {
        println(line)
    }
}

// 쓰기
File("output.txt").bufferedWriter().use { writer ->
    writer.write("첫 번째 줄")
    writer.newLine()
    writer.write("두 번째 줄")
    writer.newLine()
}

use { } 블록이 끝나면 Closeable이 자동으로 닫힙니다 (try-finally 대체).


6. 바이너리 파일 — readBytes / writeBytes

// 이미지, PDF 등 바이너리 파일
val imageBytes = File("photo.jpg").readBytes()
println("파일 크기: ${imageBytes.size} bytes")

// 복사
File("photo_copy.jpg").writeBytes(imageBytes)

// 바이트 추가
File("data.bin").appendBytes(byteArrayOf(0x00, 0x01, 0x02))

7. 파일/디렉토리 유틸리티

val file = File("config.json")

// 존재 여부 / 타입 확인
println(file.exists())      // true/false
println(file.isFile)        // 파일인지
println(file.isDirectory)   // 디렉토리인지
println(file.length())      // 바이트 크기
println(file.name)          // "config.json"
println(file.extension)     // "json"
println(file.nameWithoutExtension)  // "config"

// 디렉토리 생성
File("logs/2026").mkdirs()  // 중간 디렉토리 포함 생성

// 디렉토리 내 파일 목록
File("logs").listFiles()?.forEach { println(it.name) }

// 확장자 필터링
File("src").walk()
    .filter { it.extension == "kt" }
    .forEach { println(it.path) }

8. 실전 패턴 — CSV 파일 파싱

data class Person(val name: String, val age: Int, val email: String)

fun parseCsv(path: String): List<Person> {
    return File(path).useLines { lines ->
        lines
            .drop(1)  // 헤더 제거
            .filter { it.isNotBlank() }
            .map { line ->
                val parts = line.split(",")
                Person(
                    name = parts[0].trim(),
                    age = parts[1].trim().toInt(),
                    email = parts[2].trim()
                )
            }
            .toList()
    }
}

// 사용
val people = parseCsv("people.csv")
people.forEach { println("${it.name} (${it.age})") }

9. 실전 패턴 — 로그 파일 분석

data class LogEntry(val timestamp: String, val level: String, val message: String)

fun analyzeLog(logPath: String): Map<String, Int> {
    val levelCount = mutableMapOf<String, Int>()

    File(logPath).forEachLine { line ->
        // 예: [2026-08-01 12:00:00][ERROR] 서버 연결 실패
        val match = Regex("""\[(.+?)]\[(\w+)] (.+)""").find(line)
        if (match != null) {
            val level = match.groupValues[2]
            levelCount[level] = (levelCount[level] ?: 0) + 1
        }
    }

    return levelCount
}

val stats = analyzeLog("app.log")
stats.forEach { (level, count) -> println("$level: $count건") }
// ERROR: 5건
// WARN: 23건
// INFO: 142건

10. 정리

메서드 용도 메모리
readText() 전체 파일을 문자열로 전부 로드
readLines() 전체 줄을 List로 전부 로드
writeText() 파일에 덮어쓰기
appendText() 파일에 추가
forEachLine {} 줄 단위 스트리밍 처리 한 줄씩
useLines {} Sequence로 체이닝 처리 한 줄씩
bufferedReader() 세밀한 읽기 제어 버퍼
readBytes() 바이너리 파일 전부 로드


Related Posts