Как написать в файл в Котлин?
Я пока не могу найти этот вопрос, но каков самый простой, наиболее идиоматический способ открытия / создания файла, записи в него, а затем его закрытия? Глядя на Котлин.Ио ссылка и документация Java мне удалось получить это:
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()
}
это работает, но мне было интересно, есть ли "правильный" способ Котлина сделать это?
4 ответов
немного более идиоматические. Для издания такой пример:
File("somefile.txt").printWriter().use { out ->
history.forEach {
out.println("${it.key}, ${it.value}")
}
}
на for
петли, или forEach
зависит от вашего стиля. Нет причин использовать append(x)
так как это в основном write(x.toString())
и вы уже даете ему строку. И println(x)
в основном делает write(x)
после преобразования null
to "null"
. И println()
делает правильное окончание строки.
если вы используете data
классы Котлин, они уже могут быть выведены, потому что у них есть хороший toString()
уже метод.
кроме того, в этом случае, если вы хотели использовать BufferedWriter
это дало бы те же результаты:
File("somefile.txt").bufferedWriter().use { out ->
history.forEach {
out.write("${it.key}, ${it.value}\n")
}
}
также вы можете использовать out.newLine()
вместо \n
если вы хотите, чтобы это было правильно для текущей операционной системы, в которой он работает. И если бы вы делали это все время, вы, вероятно, создали бы функцию расширения:
fun BufferedWriter.writeLn(line: String) {
this.write(line)
this.newLine()
}
а затем используйте это вместо:
File("somefile.txt").bufferedWriter().use { out ->
history.forEach {
out.writeLn("${it.key}, ${it.value}")
}
}
и так катится Котлин. Изменение вещи в API, чтобы сделать их такими, какими вы хотите их видеть.
дико разные вкусы для этого находятся в другом ответе:https://stackoverflow.com/a/35462184/3679676
другие забавные вариации таким образом, вы можете увидеть силу Котлина:
быстрая версия, создав строку, чтобы написать все сразу:
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"))
или предполагая, что вы можете делать другие функциональные вещи, такие как фильтрующие линии или принимать только первые 100 и т. д. Вы могли бы пойти по этому маршруту:
File("somefile.txt").printWriter().use { out ->
history.map { "${it.key}, ${it.value}" }
.filter { ... }
.take(100)
.forEach { out.println(it) }
}
и Iterable
, разрешить запись в файл используя преобразование в строку, путем создания функций расширения (подобных writeText()
версия выше, но потоки контента вместо того, чтобы материализовать большую строку сначала):
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)
}
используется как любой из них:
history.entries.toFile(File("somefile.txt")) { "${it.key}, ${it.value}" }
history.entries.toFile("somefile.txt") { "${it.key}, ${it.value}" }
или используйте toString () по умолчанию для каждого элемента:
history.entries.toFile(File("somefile.txt"))
history.entries.toFile("somefile.txt")
или дали File
, позвольте заполнить его от Iterable
, создав эту функцию расширения:
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() }
}
}
использование:
File("somefile.txt").fillWith(history.entries) { "${it.key}, ${it.value}" }
или используйте toString () по умолчанию для каждого пункт:
File("somefile.txt").fillWith(history.entries)
который, если бы у вас был другой toFile
расширение уже, вы можете переписать, имея один вызов расширения другой:
fun <T: Any> File.fillWith(things: Iterable<T>, transform: (T)->String = {it.toString()}) {
things.toFile(this, transform)
}
для меня это в основном выглядит нормально. Единственное, что я хотел бы сделать, это использовать "использовать" расширение, определенное в ReadWrite для автоматического закрытия записи.
PrintWriter("file.txt").use {
for ((member, originalInput) in history) { // history: Map<Member, String>
it.append("$member, $originalInput\n")
}
}
некоторая магия Котлина позволяет опустить ссылку на поток при каждом вызове чтения или записи:
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)
}
}