본문으로 건너뛰기

📁 파일 입출력

📖 파일 입출력이란?

**파일 입출력(File I/O)**은 파일을 읽고 쓰는 작업입니다. 데이터 저장, 설정 관리, 로그 기록 등에 필수적입니다!

💡 파일 읽기

전체 읽기

import java.io.File

fun main() {
// 파일 전체를 문자열로
val content = File("data.txt").readText()
println(content)

// 파일을 줄 단위 리스트로
val lines = File("data.txt").readLines()
for (line in lines) {
println(line)
}
}

안전한 읽기

import java.io.File

fun readFileSafe(filename: String): String? {
return try {
File(filename).readText()
} catch (e: Exception) {
println("파일 읽기 실패: ${e.message}")
null
}
}

fun main() {
val content = readFileSafe("data.txt")
if (content != null) {
println("내용: $content")
} else {
println("파일을 읽을 수 없습니다")
}
}

줄 단위 처리

import java.io.File

fun main() {
// 메모리 효율적 (큰 파일에 좋음)
File("data.txt").forEachLine { line ->
println(line)
}

// useLines - 자동으로 닫힘
val lineCount = File("data.txt").useLines { lines ->
lines.count()
}
println("총 줄 수: $lineCount")
}

✏️ 파일 쓰기

덮어쓰기

import java.io.File

fun main() {
// 텍스트 쓰기
File("output.txt").writeText("안녕하세요\n코틀린입니다!")

// 줄 단위로 쓰기
val lines = listOf("첫 번째 줄", "두 번째 줄", "세 번째 줄")
File("output.txt").writeText(lines.joinToString("\n"))
}

이어쓰기

import java.io.File

fun main() {
val file = File("log.txt")

// 이어쓰기
file.appendText("로그 1\n")
file.appendText("로그 2\n")
file.appendText("로그 3\n")
}

안전한 쓰기

import java.io.File

fun writeFileSafe(filename: String, content: String): Boolean {
return try {
File(filename).writeText(content)
true
} catch (e: Exception) {
println("파일 쓰기 실패: ${e.message}")
false
}
}

fun main() {
if (writeFileSafe("data.txt", "Hello Kotlin!")) {
println("파일 저장 완료")
}
}

🎯 실전 예제

간단한 메모장

import java.io.File

class SimpleNotebook(private val filename: String) {
private val file = File(filename)

fun write(content: String) {
file.writeText(content)
println("저장 완료")
}

fun append(content: String) {
file.appendText("$content\n")
println("추가 완료")
}

fun read(): String {
return if (file.exists()) {
file.readText()
} else {
"파일이 없습니다"
}
}

fun clear() {
file.writeText("")
println("내용 삭제")
}
}

fun main() {
val notebook = SimpleNotebook("mynotes.txt")

notebook.write("첫 번째 메모")
notebook.append("두 번째 메모")
notebook.append("세 번째 메모")

println("\n=== 노트 내용 ===")
println(notebook.read())
}

로거

import java.io.File
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter

class Logger(private val filename: String) {
private val file = File(filename)
private val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")

fun log(message: String) {
val timestamp = LocalDateTime.now().format(formatter)
file.appendText("[$timestamp] $message\n")
}

fun info(message: String) = log("INFO: $message")
fun error(message: String) = log("ERROR: $message")
fun warn(message: String) = log("WARN: $message")

fun readLogs(): String {
return if (file.exists()) {
file.readText()
} else {
"로그 없음"
}
}
}

fun main() {
val logger = Logger("app.log")

logger.info("앱 시작")
logger.warn("메모리 부족")
logger.error("연결 실패")

println(logger.readLogs())
}

CSV 파일 처리

import java.io.File

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

object CsvHandler {
fun writeCsv(filename: String, people: List<Person>) {
val csv = StringBuilder()
csv.append("이름,나이,도시\n")

for (person in people) {
csv.append("${person.name},${person.age},${person.city}\n")
}

File(filename).writeText(csv.toString())
}

fun readCsv(filename: String): List<Person> {
val file = File(filename)
if (!file.exists()) return emptyList()

val lines = file.readLines()
if (lines.isEmpty()) return emptyList()

return lines.drop(1).map { line ->
val parts = line.split(",")
Person(parts[0], parts[1].toInt(), parts[2])
}
}
}

fun main() {
val people = listOf(
Person("홍길동", 25, "서울"),
Person("김철수", 30, "부산"),
Person("이영희", 28, "대구")
)

// CSV 저장
CsvHandler.writeCsv("people.csv", people)
println("CSV 저장 완료")

// CSV 읽기
val loaded = CsvHandler.readCsv("people.csv")
println("\n=== CSV 내용 ===")
loaded.forEach { println(it) }
}

설정 파일 관리

import java.io.File

class Config(private val filename: String) {
private val settings = mutableMapOf<String, String>()

init {
load()
}

private fun load() {
val file = File(filename)
if (file.exists()) {
file.forEachLine { line ->
val parts = line.split("=")
if (parts.size == 2) {
settings[parts[0].trim()] = parts[1].trim()
}
}
}
}

fun save() {
val content = settings.entries.joinToString("\n") {
"${it.key}=${it.value}"
}
File(filename).writeText(content)
}

fun set(key: String, value: String) {
settings[key] = value
}

fun get(key: String): String? {
return settings[key]
}

fun getAll(): Map<String, String> {
return settings.toMap()
}
}

fun main() {
val config = Config("app.config")

// 설정 저장
config.set("host", "localhost")
config.set("port", "8080")
config.set("timeout", "3000")
config.save()

// 설정 읽기
println("Host: ${config.get("host")}")
println("Port: ${config.get("port")}")

println("\n=== 전체 설정 ===")
config.getAll().forEach { (key, value) ->
println("$key = $value")
}
}

📂 파일/디렉토리 관리

파일 정보

import java.io.File

fun main() {
val file = File("data.txt")

println("존재: ${file.exists()}")
println("파일: ${file.isFile}")
println("디렉토리: ${file.isDirectory}")
println("크기: ${file.length()} bytes")
println("절대 경로: ${file.absolutePath}")
println("이름: ${file.name}")
println("상위 폴더: ${file.parent}")
}

디렉토리 작업

import java.io.File

fun main() {
// 디렉토리 생성
val dir = File("mydata")
dir.mkdir()

// 파일 목록
dir.listFiles()?.forEach { file ->
println("${file.name} (${if (file.isDirectory) "폴더" else "파일"})")
}

// 하위 파일 찾기
dir.walk().forEach { file ->
println(file.absolutePath)
}
}

파일 복사/삭제

import java.io.File

fun main() {
val source = File("source.txt")
val dest = File("dest.txt")

// 복사
source.copyTo(dest, overwrite = true)

// 이동
source.renameTo(File("moved.txt"))

// 삭제
dest.delete()

// 디렉토리 전체 삭제
File("mydata").deleteRecursively()
}

🤔 자주 묻는 질문

Q1. 파일이 없으면?

A: 예외 처리 필수!

fun readFileSafe(filename: String): String {
val file = File(filename)

if (!file.exists()) {
return "파일 없음"
}

return try {
file.readText()
} catch (e: Exception) {
"읽기 실패: ${e.message}"
}
}

Q2. 큰 파일은?

A: 줄 단위로 처리!

// ❌ 메모리 부족 위험
val content = File("huge.txt").readText()

// ✅ 줄 단위 처리
File("huge.txt").forEachLine { line ->
processLine(line)
}

Q3. 경로 구분자는?

A: File.separator 사용!

// ❌ OS마다 다름
val path = "data/files/text.txt"

// ✅ OS 상관없이
val file = File("data", "files").resolve("text.txt")
// 또는
val path2 = listOf("data", "files", "text.txt").joinToString(File.separator)

🎬 마치며

파일 입출력으로 데이터를 저장하고 관리하세요!

핵심 정리:
✅ readText()로 파일 읽기
✅ writeText()로 파일 쓰기
✅ forEachLine()으로 줄 단위 처리
✅ exists()로 파일 존재 확인
✅ 예외 처리 필수

다음 단계: 정규표현식에서 패턴 매칭을 알아보세요!