kotlin - reales - libro de android studio en español pdf
¿Cómo escribo en un archivo en Kotlin? (4)
Cierta magia de Kotlin permite omitir hacer referencia a la transmisión en cada llamada de lectura o escritura:
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)
}
}
Parece que aún no puedo encontrar esta pregunta, pero ¿cuál es la forma más simple e idiomática de abrir / crear un archivo, escribir en él y luego cerrarlo? Mirando la referencia de kotlin.io y la documentación de Java logré obtener esto:
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()
}
Esto funciona, pero me preguntaba si existía una forma "correcta" de Kotlin de hacer esto.
En su mayoría se ve bien para mí. Lo único diferente que haría es usar la extensión " use " definida en ReadWrite para cerrar automáticamente el escritor.
PrintWriter("file.txt").use {
for ((member, originalInput) in history) { // history: Map<Member, String>
it.append("$member, $originalInput/n")
}
}
Un poco más idiomático. Para PrintWriter, este ejemplo:
File("somefile.txt").printWriter().use { out ->
history.forEach {
out.println("${it.key}, ${it.value}")
}
}
El ciclo for
, o forEach
depende de tu estilo. No hay razón para usar append(x)
ya que es básicamente write(x.toString())
y ya le da una cadena. Y println(x)
básicamente write(x)
después de convertir un null
en "null"
. Y println()
hace el final de línea correcto.
Si está utilizando clases de data
de Kotlin, ya pueden salir porque ya tienen un buen método toString()
.
Además, en este caso, si quisiera usar BufferedWriter
, produciría los mismos resultados:
File("somefile.txt").bufferedWriter().use { out ->
history.forEach {
out.write("${it.key}, ${it.value}/n")
}
}
También puede usar out.newLine()
lugar de /n
si desea que sea correcto para el sistema operativo actual en el que se está ejecutando. Y si estuvieras haciendo eso todo el tiempo, probablemente crearías una función de extensión:
fun BufferedWriter.writeLn(line: String) {
this.write(line)
this.newLine()
}
Y luego usa eso en su lugar:
File("somefile.txt").bufferedWriter().use { out ->
history.forEach {
out.writeLn("${it.key}, ${it.value}")
}
}
Y así es como Kotlin rueda. Cambia las cosas en las API para que sean como quieres que sean.
Sorprendentemente diferentes sabores para esto se encuentran en otra respuesta: https://.com/a/35462184/3679676
Otras variaciones divertidas para que pueda ver el poder de Kotlin:
Una versión rápida creando la cadena para escribir todo de una vez:
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"))
O suponiendo que puede hacer otras cosas funcionales como líneas de filtro o tomar solo los primeros 100, etc. Podría seguir esta ruta:
File("somefile.txt").printWriter().use { out ->
history.map { "${it.key}, ${it.value}" }
.filter { ... }
.take(100)
.forEach { out.println(it) }
}
O dado un Iterable
, permite escribirlo en un archivo usando una transformación en una cadena, creando funciones de extensión (similar a la versión anterior de writeText()
, pero transmite el contenido en lugar de materializar primero una cadena grande):
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)
}
utilizado como cualquiera de estos:
history.entries.toFile(File("somefile.txt")) { "${it.key}, ${it.value}" }
history.entries.toFile("somefile.txt") { "${it.key}, ${it.value}" }
o use default toString () en cada elemento:
history.entries.toFile(File("somefile.txt"))
history.entries.toFile("somefile.txt")
O dado un File
, permita llenarlo desde un Iterable
, al crear esta función de extensión:
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 el uso de:
File("somefile.txt").fillWith(history.entries) { "${it.key}, ${it.value}" }
o use default toString () en cada elemento:
File("somefile.txt").fillWith(history.entries)
que si ya tuviera la otra extensión de toFile
, podría reescribir una llamada de extensión con la otra:
fun <T: Any> File.fillWith(things: Iterable<T>, transform: (T)->String = {it.toString()}) {
things.toFile(this, transform)
}