18-bo‘lim

Fayllar bilan ishlash

Faylga yozish va o'qish, qatorlar bilan ishlash, katta fayllar uchun useLines, papkalar, use bilan resursni yopish va oddiy CSV tahlili.

🕑 11 daqiqa o‘qish 📄 526 so‘z 👁 0 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. Eng oddiy yozish va o'qish
  2. Qatorlar bilan
  3. Katta fayllar - useLines
  4. use - resursni avtomatik yopish
  5. Yo'llar va papkalar
  6. Fayl nomining qismlari
  7. Oddiy CSV
  8. Yozib chiqarish
  9. Xatolarni hisobga olish
  10. Xulosa

Dastur to'xtaganda xotiradagi hamma narsa yo'qoladi. Ma'lumotni saqlab qolish uchun uni faylga yozish kerak.

Kotlin da bu Java ga qaraganda ancha qisqa - File sinfiga kengaytmalar qo'shilgan.

Eng oddiy yozish va o'qish #

KOTLIN
import java.io.File

fun main() {
    val fayl = File("qaydlar.txt")

    fayl.writeText("Birinchi qator\nIkkinchi qator")
    println(fayl.readText())

    fayl.appendText("\nUchinchi qator")
    println("---")
    println(fayl.readText())

    fayl.delete()
    println(fayl.exists())
}
Natija
Birinchi qator
Ikkinchi qator
---
Birinchi qator
Ikkinchi qator
Uchinchi qator
false
MetodNima qiladi
writeText(s)Faylga yozadi (ustiga yozadi)
appendText(s)Oxiriga qo'shadi
readText()Butun faylni satr qilib o'qiydi
exists()Fayl bormi
delete()O'chiradi
writeText eski mazmunni yo'q qiladi

writeText mavjud faylni so'ramasdan ustiga yozadi. Qo'shish kerak bo'lsa appendText, yoki avval exists() bilan tekshiring.

Bu ayniqsa foydalanuvchi ma'lumoti bilan ishlaganda xavfli.

Qatorlar bilan #

KOTLIN
import java.io.File

fun main() {
    val fayl = File("talabalar.txt")
    val royxat = listOf("Malika", "Husanboy", "Nodira", "Aziza")

    fayl.writeText(royxat.joinToString("\n"))

    val oqilgan = fayl.readLines()
    println("Jami: ${oqilgan.size}")

    oqilgan.forEachIndexed { i, ism ->
        println("${i + 1}. $ism")
    }

    println(oqilgan.filter { it.startsWith("A") })

    fayl.delete()
}
Natija
Jami: 4
1. Malika
2. Husanboy
3. Nodira
4. Aziza
[Aziza]

readLines() ro'yxat qaytaradi - shuning uchun 10-bo'limdagi hamma funksiya darhol ishlaydi.

Katta fayllar - useLines #

KOTLIN
import java.io.File

fun main() {
    val fayl = File("sonlar.txt")
    fayl.writeText((1..1000).joinToString("\n"))

    val jami = fayl.useLines { qatorlar ->
        qatorlar.map { it.toInt() }.filter { it % 3 == 0 }.sum()
    }
    println("3 ga bo'linadiganlar yig'indisi: $jami")

    var soni = 0
    fayl.forEachLine { soni++ }
    println("Qatorlar: $soni")

    fayl.delete()
}
Natija
3 ga bo'linadiganlar yig'indisi: 166833
Qatorlar: 1000
readText va useLines farqi

readText() butun faylni xotiraga yuklaydi. 10 KB uchun bu normal, 2 GB uchun esa dastur qulaydi.

useLines faylni ketma-ketlik sifatida beradi: qatorlar birma-bir o'qiladi va xotirada faqat bittasi turadi. Blok tugagach fayl avtomatik yopiladi.

Qoida: fayl hajmini oldindan bilmasangiz - useLines.

use - resursni avtomatik yopish #

KOTLIN
import java.io.File

fun main() {
    val fayl = File("hisobot.txt")

    fayl.bufferedWriter().use { yozuvchi ->
        yozuvchi.write("Sarlavha")
        yozuvchi.newLine()
        repeat(3) { i ->
            yozuvchi.write("Qator ${i + 1}")
            yozuvchi.newLine()
        }
    }

    fayl.bufferedReader().use { oquvchi ->
        println(oquvchi.readText().trim())
    }

    fayl.delete()
}
Natija
Sarlavha
Qator 1
Qator 2
Qator 3

use { } - blok tugaganda (xato bo'lsa ham) resursni avtomatik yopadi. Bu Java dagi try-with-resources ning Kotlin shakli.

Fayl va ulanish uchun har doim use

use yozmasangiz va xato yuz bersa, fayl ochiq qolishi mumkin. Keyin uni boshqa dastur o'chira ham, o'zgartira ham olmaydi.

use har qanday Closeable bilan ishlaydi - fayl, tarmoq ulanishi, ma'lumotlar bazasi.

Yo'llar va papkalar #

KOTLIN
import java.io.File

fun main() {
    val papka = File("sinov_papka")
    papka.mkdirs()
    println(papka.exists())

    File(papka, "a.txt").writeText("A")
    File(papka, "b.txt").writeText("B")

    val fayllar = papka.listFiles()?.sortedBy { it.name } ?: emptyList()
    println("Fayllar: ${fayllar.size}")
    fayllar.forEach { println("  ${it.name} (${it.length()} bayt)") }

    papka.deleteRecursively()
    println(papka.exists())
}
Natija
true
Fayllar: 2
  a.txt (1 bayt)
  b.txt (1 bayt)
false

Diqqat: listFiles() null qaytarishi mumkin (papka bo'lmasa yoki ruxsat yetmasa), shuning uchun ?: bilan himoyalangan.

Va yana: u tartibni kafolatlamaydi - sortedBy qo'shildi.

Fayl nomining qismlari #

KOTLIN
import java.io.File

fun main() {
    val fayl = File("hujjatlar/hisobot.pdf")

    println(fayl.name)
    println(fayl.nameWithoutExtension)
    println(fayl.extension)
    println(fayl.parent)
    println(fayl.path.replace("\\", "/"))
}
Natija
hisobot.pdf
hisobot
pdf
hujjatlar
hujjatlar/hisobot.pdf

replace("\", "/") faqat shu darslik uchun - natija har ikkala tizimda bir xil ko'rinishi kerak edi. O'z kodingizda bu kerak emas.

Oddiy CSV #

KOTLIN
import java.io.File

data class Talaba(val ism: String, val ball: Int, val guruh: String)

fun oqi(fayl: File): List<Talaba> =
    fayl.readLines()
        .drop(1)
        .filter { it.isNotBlank() }
        .mapNotNull { qator ->
            val qismlar = qator.split(",").map { it.trim() }
            if (qismlar.size != 3) return@mapNotNull null
            val ball = qismlar[1].toIntOrNull() ?: return@mapNotNull null
            Talaba(qismlar[0], ball, qismlar[2])
        }

fun main() {
    val fayl = File("talabalar.csv")
    fayl.writeText(
        """
        ism,ball,guruh
        Malika,88,A
        Husanboy,92,B
        Nodira,xato,A
        Aziza,95,B
        """.trimIndent()
    )

    val talabalar = oqi(fayl)
    println("O'qildi: ${talabalar.size} ta")
    talabalar.forEach { println("  $it") }

    println("O'rtacha: ${talabalar.map { it.ball }.average()}")

    fayl.delete()
}
Natija
O'qildi: 3 ta
  Talaba(ism=Malika, ball=88, guruh=A)
  Talaba(ism=Husanboy, ball=92, guruh=B)
  Talaba(ism=Aziza, ball=95, guruh=B)
O'rtacha: 91.66666666666667

Buzuq qator (Nodira,xato,A) jimgina tashlab yuborildi - mapNotNull null qaytargan elementlarni olib tashlaydi.

return@mapNotNull - lambda dan chiqish usuli. Oddiy return butun funksiyadan chiqarib yuborardi.

Yozib chiqarish #

KOTLIN
import java.io.File

data class Mahsulot(val nom: String, val narx: Int)

fun main() {
    val ombor = listOf(
        Mahsulot("Olma", 12000),
        Mahsulot("Anor", 18000),
        Mahsulot("Uzum", 22000)
    )

    val fayl = File("ombor.csv")

    fayl.bufferedWriter().use { w ->
        w.write("nom,narx")
        w.newLine()
        ombor.forEach {
            w.write("${it.nom},${it.narx}")
            w.newLine()
        }
    }

    println(fayl.readText().trim())

    fayl.delete()
}
Natija
nom,narx
Olma,12000
Anor,18000
Uzum,22000

Xatolarni hisobga olish #

KOTLIN
import java.io.File

fun xavfsizOqi(yol: String): String {
    val fayl = File(yol)
    if (!fayl.exists()) return "Fayl topilmadi: $yol"
    return runCatching { fayl.readText() }
        .getOrElse { "O'qib bo'lmadi: ${it::class.simpleName}" }
}

fun main() {
    val fayl = File("bor.txt")
    fayl.writeText("mazmun")

    println(xavfsizOqi("bor.txt"))
    println(xavfsizOqi("yoq.txt"))

    fayl.delete()
}
Natija
mazmun
Fayl topilmadi: yoq.txt

Fayl bilan ishlashda hamma narsa noto'g'ri ketishi mumkin: fayl o'chirilgan, ruxsat yo'q, disk to'lgan. Shuning uchun tekshiruv va runCatching birga ishlatiladi.

JSON haqida

Kotlin da JSON uchun kotlinx.serialization kutubxonasi ishlatiladi:

KOTLIN
@Serializable
data class Talaba(val ism: String, val ball: Int)

val json = Json.encodeToString(talaba)
val qayta = Json.decodeFromString<Talaba>(json)

U data class bilan juda yaxshi ishlaydi, lekin loyihaga qo'shimcha plagin va bog'liqlik talab qiladi - shuning uchun bu darslikda ishlatilmadi.

Gradle loyihasida kotlin("plugin.serialization") plaginini va kotlinx-serialization-json bog'liqligini qo'shsangiz, yuqoridagi kod ishlaydi.

Amaliy topshiriq
  1. Matnni faylga yozib, qayta o'qing.
  2. appendText bilan yangi qator qo'shing.
  3. readLines() bilan o'qib, filter qo'llang.
  4. useLines bilan katta faylni qayta ishlang.
  5. use { } bilan yozing va uni tushirib qoldirib ko'ring.
  6. Papka yaratib, ichiga ikkita fayl yozing.
  7. listFiles() natijasini saralab chiqaring.
  8. Fayl kengaytmasini extension bilan oling.
  9. CSV faylni o'qib, data class ro'yxatiga aylantiring.
  10. Mavjud bo'lmagan faylni o'qishga urinib, xatoni ushlang.

Xulosa #

  • File("nom").writeText() ustiga yozadi; qo'shish uchun appendText.
  • readLines() ro'yxat beradi - kolleksiya funksiyalari darhol ishlaydi.
  • Katta fayllar uchun useLines yoki forEachLine.
  • use { } resursni xato bo'lsa ham avtomatik yopadi.
  • listFiles() null qaytarishi mumkin va tartibni kafolatlamaydi.
  • mapNotNull + return@mapNotNull - buzuq qatorlarni tashlab ketishning toza usuli.
  • Fayl amallarini exists() tekshiruvi va runCatching bilan himoyalang.

Keyingi bo'limda korutinlarni o'rganamiz.

Xatolik topdingizmi?

Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.