2016-02-16 15 views
27

Non riesco a trovare questa domanda ancora, ma qual è il modo più semplice e più idiomatico di aprire/creare un file, scriverci sopra e poi chiuderlo? Guardando al riferimento kotlin.io e la documentazione Java sono riuscito ad ottenere questo:Come scrivere su un file in Kotlin?

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() 
} 

questo funziona, ma mi chiedevo se ci fosse un "proprio" modo Kotlin di fare questo?

+2

Ho aggiunto più varianti in una nuova risposta per divertimento, per darti più idee su ciò che puoi fare. –

risposta

49

Un po 'più idiomatico. Per PrintWriter, questo esempio:

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

Il for ciclo, o forEach dipende dal vostro stile. Non c'è motivo di usare append(x) dato che è fondamentalmente write(x.toString()) e gli hai già dato una stringa. E println(x) fondamentalmente fa write(x) dopo aver convertito un null in "null". E println() termina la riga corretta.

Se si utilizzano le classi data di Kotlin, possono già essere emesse perché hanno già un buon metodo toString().

Inoltre, in questo caso, se si voleva utilizzare BufferedWriter produrrebbe gli stessi risultati:

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

Inoltre è possibile utilizzare out.newLine() invece di \n se si vuole che sia corretto per il sistema operativo corrente in che sta funzionando. E se si stesse facendo che tutto il tempo, si sarebbe probabilmente creare una funzione di estensione:

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

e quindi utilizzare che, invece:

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

ed è così che rotoli Kotlin. Cambia le cose nelle API per renderle come vuoi che siano.

sapori molto diversi per questo sono in un'altra risposta: https://stackoverflow.com/a/35462184/3679676

+1

grazie per questo e le altre varianti più "divertenti"! +1 – yiwei

+1

può un'altra soluzione possibile: File (nomeFile) .writeText (Iterable .joinToString(), Charsets.UTF_8) –

+0

@IstvanNagy a seconda della dimensione del iterabile e se si desidera materializzare la stringa completa prima di scrivere. Lo aggiungerò alla risposta alternativa, è bello e breve! –

5

Mi sembra tutto ok. L'unica cosa che farei è usare l'estensione "use" definita in ReadWrite per chiudere automaticamente il writer.

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

Altre variazioni divertenti in modo da poter vedere la potenza di Kotlin:

Una versione rapida con la creazione della stringa di scrivere tutto in una volta:

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")) 

oppure si supponendo che potrebbe fare altre cose funzionali come le linee di filtro o prendere solo il primo 100, ecc.Si potrebbe seguire questa strada:

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

o dato un Iterable, consentire la scrittura in un file utilizzando una trasformazione in una stringa, con la creazione di funzioni di estensione (simile a writeText() versione precedente, ma i flussi il contenuto, invece di materializzando una grande stringa prima):

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) 
} 

usato come uno di questi:

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

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

o toString uso predefinito() su ogni articolo:

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

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

Oppure dato un File, permettono il riempimento da un Iterable, creando la funzione di estensione:

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() } 
    } 
} 

con l'uso di:

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

o l'uso toString di default() su ogni elemento:

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

che se si ha già l'altro toFile estensione, si potrebbe riscrivere dover chiamare un interno all'altro:

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

https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.io/java.io.-file/write-text.html –

0

qualche magia Kotlin consente di omettere riferimento al flusso su ogni lettura o scrittura chiamare:

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) 
    } 
}