web-dev-qa-db-fra.com

Comment écrire dans un fichier Kotlin?

Je n'arrive pas encore à trouver cette question, mais quel est le moyen le plus simple et le plus idiomatique d'ouvrir/créer un fichier, de l'écrire puis de le fermer? En regardant la référence kotlin.io et la documentation 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, $originalInput\n")
    }

    writer.close()
}

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

39
yiwei

Un peu plus idiomatique. Pour PrintWriter, cet exemple:

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

La boucle for ou forEach dépend de votre style. Aucune raison d'utiliser append(x) puisqu'il s'agit essentiellement de write(x.toString()) et que vous lui attribuez déjà une chaîne. Et println(x) exécute fondamentalement write(x) après la conversion d'une null en "null". Et println() fait la bonne ligne se terminant.

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

De plus, dans ce cas, si vous vouliez utiliser BufferedWriter, les mêmes résultats seraient obtenus:

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

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

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

Et utilisez ensuite cela à la place:

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

Et c'est comme ça que Kotlin roule. Changez les choses dans les API pour les rendre comme vous le souhaitez.

Des réponses très différentes sont proposées dans une autre réponse: https://stackoverflow.com/a/35462184/3679676

78
Jayson Minard

Autres variations amusantes pour que vous puissiez voir la puissance de Kotlin:

Une version rapide en créant la chaîne pour tout écrire en une 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 fassiez d'autres tâches fonctionnelles, telles que les lignes de filtrage, ou que vous ne preniez que les 100 premiers, etc. Vous pouvez choisir cette voie:

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

Ou étant donné une Iterable, autoriser l'écrire dans un fichier en utilisant une transformation en chaîne, en créant des fonctions d'extension (similaire à la version writeText() ci-dessus, mais diffuse le contenu au lieu de matérialiser une chaîne importante):

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 ceux-ci:

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

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

ou utilisez default toString () sur chaque élément:

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

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

Ou avec une File, permet de la remplir à partir d'une 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() }
    }
}

avec utilisation de:

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

ou utilisez default toString () sur chaque élément:

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

qui si vous avez déjà l’autre extension toFile, vous pouvez réécrire en ayant un poste appeler l’autre:

fun <T: Any> File.fillWith(things: Iterable<T>, transform: (T)->String = {it.toString()}) {
    things.toFile(this, transform)
}
21
Jayson Minard

Cela me semble plutôt bien. La seule chose différente que je ferais serait d'utiliser l'extension " use " définie dans ReadWrite pour fermer automatiquement le writer.

PrintWriter("file.txt").use {
  for ((member, originalInput) in history) {  // history: Map<Member, String>
    it.append("$member, $originalInput\n")
  }    
}
5
Lionel Port

Certaines magies Kotlin permettent d’omettre le référencement du flux à chaque appel en lecture ou en é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
Vadzim
try{
      val fileWriter = FileWriter("test.txt", true)
      fileWriter.write(string+ "\n")
      fileWriter.close()
    } catch (exception: Exception){
        println(exception.message)
    }
0
Ajay Prajapati