Comment écrire dans un fichier de Kotlin?

Je n'arrive pas encore à trouver cette question, mais quelle est la façon la plus simple, la plus idiomatique d'ouvrir / créer un fichier, de lui écrire, puis de le fermer? En regardant l' kotlin.io référence et la documentation de Java, j'ai réussi à obtenir ceci:

fun write() {
    val writer = PrintWriter("file.txt")  // java.io.PrintWriter

    for ((member, originalInput) in history) {  // history: Map<Member, String>
        writer.append("$member, $originalInputn")
    }

    writer.close()
}

cela fonctionne, mais je me demandais s'il y avait une "bonne" façon Kotlin de faire cela?

34
demandé sur Istvan Nagy 2016-02-17 01:29:00

4 réponses

Un peu plus idiomatique. Pour PrintWriter, cet exemple:

File("somefile.txt").printWriter().use { out ->
    history.forEach {
        out.println("${it.key}, ${it.value}")
    }
}

for boucle, ou forEach dépend de votre style. Pas de raison d'utiliser append(x) puisque c'est fondamentalement write(x.toString()) et vous lui donnez déjà une ficelle. Et println(x) en gros fait write(x) après conversion d'un null"null". Et println() fait la fin de ligne correcte.

Si vous utilisez data les classes de Kotlin, elles peuvent déjà être sorties car elles ont un beau toString() méthode déjà.

Aussi, dans ce cas, si vous vouliez utiliser BufferedWriter il serait de produire les mêmes résultats:

File("somefile.txt").bufferedWriter().use { out ->
    history.forEach {
        out.write("${it.key}, ${it.value}\n")
    }
}

vous pouvez aussi utiliser out.newLine() au lieu de \n si vous voulez qu'il soit correct pour le système d'exploitation actuel dans lequel il s'exécute. Et si vous faisiez cela tout le temps, vous créeriez probablement une fonction d'extension:

fun BufferedWriter.writeLn(line: String) {
    this.write(line)
    this.newLine()
}

Et ensuite l'utiliser à la place:

File("somefile.txt").bufferedWriter().use { out ->
    history.forEach {
        out.writeLn("${it.key}, ${it.value}")
    }
}

Et voilà comment Kotlin rouleaux. Changement les choses dans les API pour les rendre comme vous voulez qu'elles soient.

très différentes saveurs pour ce sont dans une autre réponse: https://stackoverflow.com/a/35462184/3679676

71
répondu Jayson Minard 2017-05-23 12:02:50

autres variations amusantes donc vous pouvez voir le pouvoir de Kotlin:

une version rapide en créant la chaîne pour écrire tout à la fois:

File("somefile.txt").writeText(history.entries.joinToString("\n") { "${it.key}, ${it.value}" })
// or just use the toString() method without transform:
File("somefile.txt").writeText(x.entries.joinToString("\n"))

ou en supposant que vous pourriez faire d'autres choses fonctionnelles comme des lignes de filtre ou ne prendre que les 100 premières, etc. Vous pourriez aller dans cette voie:

File("somefile.txt").printWriter().use { out ->
    history.map { "${it.key}, ${it.value}" }
           .filter { ... }
           .take(100)
           .forEach { out.println(it) }
}

Ou un Iterable, d'autoriser l'écriture dans un fichier en utilisant une transform à une chaîne, en créant des fonctions d'extension (similaires à writeText() version ci-dessus, mais diffuse le contenu au lieu de matérialiser une grosse chaîne en premier):

fun <T: Any> Iterable<T>.toFile(output: File, transform: (T)->String = {it.toString()}) {
    output.bufferedWriter().use { out ->
        this.map(transform).forEach { out.write(it); out.newLine() }
    }
}

fun <T: Any> Iterable<T>.toFile(outputFilename: String, transform: (T)->String = {it.toString()}) {
    this.toFile(File(outputFilename), transform)
}

utilisé comme l'un de ces:

history.entries.toFile(File("somefile.txt")) {  "${it.key}, ${it.value}" }

history.entries.toFile("somefile.txt") {  "${it.key}, ${it.value}" }

ou utiliser la valeur par défaut toString() sur chaque élément:

history.entries.toFile(File("somefile.txt")) 

history.entries.toFile("somefile.txt") 

Ou d'un File, le remplissage à partir d'un Iterable, en créant cette fonction d'extension:

fun <T: Any> File.fillWith(things: Iterable<T>, transform: (T)->String = {it.toString()}) {
    this.bufferedWriter().use { out ->
        things.map(transform).forEach { out.write(it); out.newLine() }
    }
}

utilisation:

File("somefile.txt").fillWith(history.entries) { "${it.key}, ${it.value}" }

ou utilisez par défaut toString () sur chaque élément:

File("somefile.txt").fillWith(history.entries) 

si vous aviez l'autre toFile extension déjà, vous pouvez réécrire avoir une extension d'appel de l'autre:

fun <T: Any> File.fillWith(things: Iterable<T>, transform: (T)->String = {it.toString()}) {
    things.toFile(this, transform)
}
20
répondu Jayson Minard 2016-02-18 12:15:48

Il a surtout l'air bon pour moi. La seule chose que je voudrais faire est d'utiliser le "utiliser" extension définie dans ReadWrite pour fermer automatiquement l'auteur.

PrintWriter("file.txt").use {
  for ((member, originalInput) in history) {  // history: Map<Member, String>
    it.append("$member, $originalInput\n")
  }    
}
5
répondu Lionel Port 2016-02-16 22:42:58

un peu de magie Kotlin permet d'omettre le référencement du flux sur chaque appel de lecture ou d'écriture:

fun <T : Closeable, R> T.useWith(block: T.() -> R): R = use { with(it, block) }

File("a.in").bufferedReader().useWith {
    File("a.out").printWriter().useWith {
        val (a, b) = readLine()!!.split(' ').map(String::toInt)
        println(a + b)
    }
}

Scanner(File("b.in")).useWith {
    PrintWriter("b.out").useWith {
        val a = nextInt()
        val b = nextInt()
        println(a + b)
    }
}
1
répondu Vadzim 2017-05-23 18:04:50