Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Lekce 3 - Práce s textovými soubory v Kotlin

V minulém dílu, Třídy pro práci se soubory v Kotlin, jsme si ukázali, jak fungují přístupová práva v operačních systémech a jak se vytvářejí instance tříd Path a File.

V dnešním Kotlin tutoriálu si vysvětlíme práci s textovými soubory. Ukážeme si opět, jak můžeme využít obě API Javy, tedy starší java.io.* a novější java.nio.*.

Nejjednodušší cestou, jak uložit data aplikace na pevný disk, je využít textové soubory. Se soubory s příponou .txt jsme se jistě všichni již setkali. Text je v nich uložen jednoduše na jednotlivých řádcích. K oddělení řádků se využívá speciálních znaků, které jsou bohužel specifické pro každý operační systém. Toto však za nás naštěstí vyřeší Kotlin.

API java.io

Pojďme se nejprve podívat na starší způsob práce se soubory pomocí java.io.*.

Zápis do nového textového souboru

Vytvoříme si nový projekt s názvem TextoveSoubory. K zápisu do textového souboru slouží třída FileWriter, která se ale obvykle obaluje do třídy BufferedWriter. Objekt BufferedWriter používá k zápisu na disk vyrovnávací paměť a je tudíž výrazně výkonnější. Také poskytuje metodu newLine(). Tato metoda vloží do souboru správný znak konce řádku, nezávisle na operačním systému.

Nejprve si ale vytvoříme instanci třídy File, tak jak jsme si to ukázali v lekci Třídy pro práci se soubory. Soubor pojmenujeme třeba oldapi.txt:

val file = File(System.getProperty("user.home") + File.separator + "itnetwork" + File.separator + "oldapi.txt")
file.parentFile
    .mkdirs()

Nyní vytvoříme blok try-with-resources a v něm novou instanci typu BufferedWriter. Jak již víme z předchozích dílů, blok try-with-resources se nám automaticky postará o zavření souboru po dokončení zápisu/čtení. Do konstruktoru vložíme instanci objektu FileWriter. Samotný objekt FileWriter potřebuje objekt typu File, který jsme vytvořili výše.

try {
     BufferedWriter(FileWriter(file)).use { bw ->

     }
 } catch (e: Exception) {
     println("Do souboru se nepovedlo zapsat.")
 }

Náš objekt BufferedWriter je nyní nasměrovaný na správný soubor. Nový řádek zapíšeme pomocí metody write(). Odřádkování v souboru docílíme metodou newLine(). Po dokončení zápisu musíme zavolat metodu flush(), která se stará o vyprázdnění bufferu. S tím se zde nebudeme zatěžovat, postačí nám vědět, že námi zapsané řádky mohou zůstat chvíli ve vyrovnávací paměti a my pomocí flush() vynutíme jejich zápis.

Kód se nám tedy rozrostl a vypadá takto:

val file = File(System.getProperty("user.home") + File.separator + "itnetwork" + File.separator + "oldapi.txt")
file.parentFile
    .mkdirs()

try {
    BufferedWriter(FileWriter(file)).use { bw ->
        bw.write("První řádek")
        bw.newLine()
        bw.write("Tento text je na druhém řádku")
        bw.newLine()
        bw.write("A do třetice.")
        bw.newLine()
        bw.flush()
        println("Do souboru bylo zapsáno")
    }
} catch (e: Exception) {
    println("Do souboru se nepovedlo zapsat.")
}

Po spuštění se vytvoří soubor oldapi.txt v našem domovském adresáři v podadresáři itnetwork\. Můžeme se tam podívat a běžným textovým editorem se přesvědčit, že opravdu obsahuje náš text:

Soubory a práce s nimi v Kotlin

Připsání textu do existujícího souboru

Takto do existujícího souboru připíšeme nový řádek:

try {
    BufferedWriter(FileWriter(file, true)).use { bw ->
        bw.write("Připsaný řádek")
        bw.newLine()
        bw.flush()
        println("Do souboru bylo připsáno")
    }
} catch (e: Exception) {
    println("Do souboru se nepovedlo zapsat.")
}

Druhým parametrem konstruktoru objektu FileWriter s hodnotou true, specifikujeme připsání do existujícího souboru, jehož původní obsah zůstane nezměněn.

Čtení existujícího souboru

Zbývá nám již jen umět soubor načíst. Není to o nic složitější, než zápis a opět k tomu máme v Javě připravenou třídu, konkrétně BufferedReader. Použití je obdobné, namísto metody write() použijeme metodu readLine(), která vrací řádek textu ze souboru a zároveň se přesune na řádek následující. Budeme ji tedy volat v cyklu while. Podmínka pro ošetření vyjetí ze souboru je možná krkolomnější, kontrolujeme, zda proběhlo přiřazení nové řádky do proměnné.

Kód k výpisu obsahu souboru do konzole vypadá takto:

try {
    BufferedReader(FileReader(file)).use { br ->
        var s: String?
        while (br.readLine().also { s = it } != null) {
            println(s)
        }
    }
} catch (e: Exception) {
    println("Chyba při čtení ze souboru.")
}

Kód celého našeho programu vypadá takto:

// zápis do souboru
    val file = File(System.getProperty("user.home") + File.separator + "itnetwork" + File.separator + "oldapi.txt")
    file.parentFile
        .mkdirs()

    try {
        BufferedWriter(FileWriter(file)).use { bw ->
            bw.write("První řádek")
            bw.newLine()
            bw.write("Tento text je na druhém řádku")
            bw.newLine()
            bw.write("A do třetice.")
            bw.newLine()
            bw.flush()
            println("Do souboru bylo zapsáno")
        }
    } catch (e: Exception) {
        println("Do souboru se nepovedlo zapsat.")
    }

    // připsání textu do existujícího souboru
    try {
        BufferedWriter(FileWriter(file, true)).use { bw ->
            bw.write("Připsaný řádek")
            bw.newLine()
            bw.flush()
            println("Do souboru bylo připsáno")
        }
    } catch (e: Exception) {
        println("Do souboru se nepovedlo zapsat.")
    }

    // výpis obsahu souboru
    println("Vypisuji celý soubor:")
    try {
        BufferedReader(FileReader(file)).use { br ->
            var s: String?
            while (br.readLine().also { s = it } != null) {
                println(s)
            }
        }
    } catch (e: Exception) {
        println("Chyba při čtení ze souboru.")
    }

A výsledek:

Do souboru bylo zapsáno
Do souboru bylo připsáno
Vypisuji celý soubor:
První řádek
Tento text je na druhém řádku
A do třetice.
Připsaný řádek

API java.nio

Teď se podíváme na novější způsob práce se soubory pomocí java.nio.*.

Zápis do nového textového souboru

Nejprve si vytvoříme instanci třídy Path. Soubor pojmenujeme třeba newapi.txt:

val path = Path.of(System.getProperty("user.home"), "itnetwork", "newapi.txt")

K zápisu do souboru použijeme statickou metodu writeString() ze třídy Files. Nezapomeneme nejprve vytvořit všechny potřebné adresáře a zachytávat případnou výjimku. Jako oddělovač řádku použijeme univerzální oddělovač pomocí metody lineSeparator().

Metoda writeString() přijímá dva a více parametrů. První parametr je typu Path, čili soubor, do kterého chceme zapisovat. Druhý parametr je typu String, čili text, který chceme zapisovat. Poté následují nepovinné parametry typu StandardOpenOption, kterými metodě říkáme, jakým způsobem se má soubor otevřít.

Způsoby otevření souboru

Nejpoužívanější volby výčtového typu StandardOpenOption pro způsoby otevření souboru jsou tyto:

  • CREATE - vytvoří nový soubor, nevyhazuje chybu, pokud už existuje,
  • CREATE_NEW - vytvoří nový soubor, vyhodí chybu, pokud soubor už existuje,
  • TRUNCATE_EXISTING - při otevření vymaže obsah souboru,
  • APPEND - otevře soubor pro přidávání obsahu.

V následujícím příkladu při zápisu prvního řádku použijeme volbu CREATE a TRUNCATE_EXISTING, aby se soubor vytvořil a existující soubor napřed vymazal. Každý další zápis už děláme pomocí volby APPEND, aby se zapisovaný text typu String přidal na konec souboru:

try {
    Files.createDirectories(path.parent)
    Files.writeString(
        path,
        "První řádek" + System.lineSeparator(),
        StandardOpenOption.CREATE,
        StandardOpenOption.TRUNCATE_EXISTING
    )

} catch (ex: IOException) {
    println("Chyba při zápisu do souboru: " + ex.message)
}

Stejně, jako v případě staršího API java.io, se nám i nyní po spuštění programu vytvoří nový soubor s názvem newapi.txt obsahující tři řádky textu. Můžeme si to ověřit libovolným textovým editorem.

Připsání textu do existujícího souboru

Připsání řádků do již existujícího souboru děláme pomocí volby APPEND:

try {
    Files.writeString(path, "Tento text je na druhém řádku" + System.lineSeparator(), StandardOpenOption.APPEND)
        Files.writeString(path, "A do třetice" + System.lineSeparator(), StandardOpenOption.APPEND)
    } catch (IOException ex) {
    System.out.println("Chyba při zápisu do souboru: " + ex.message());
}

Čtení existujícího souboru

Ke čtení řádků z textového souboru použijeme metodu readAllLines() opět ze třídy Files. Tato metoda vrací kolekci List<String>:

try {
    val lines = Files.readAllLines(path)
    lines.forEach { x: String? -> println(x) }
} catch (ex: IOException) {
    println("Chyba při čtení souboru: " + ex.message)
}

Nově vytvořený soubor newapi.txt opět nalezneme v domovském adresáři v podadresáři itnetwork\ a přesvědčíme se, že opravdu obsahuje náš text.

V příští lekci , Práce s CSV soubory v Kotlin - Uložení objektů, vytvoříme formulářovou aplikaci s databází uživatelů a naučíme se ukládat instance uživatelů do textových souborů ve formátu CSV.


 

Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.

Stáhnout

Stažením následujícího souboru souhlasíš s licenčními podmínkami

Staženo 6x (6.9 MB)
Aplikace je včetně zdrojových kódů v jazyce Kotlin

 

Předchozí článek
Třídy pro práci se soubory v Kotlin
Všechny články v sekci
Soubory a práce s nimi v Kotlin
Přeskočit článek
(nedoporučujeme)
Práce s CSV soubory v Kotlin - Uložení objektů
Článek pro vás napsal Filip Studený
Avatar
Uživatelské hodnocení:
3 hlasů
.
Aktivity