Как написать в файл в Котлин?

Я пока не могу найти этот вопрос, но каков самый простой, наиболее идиоматический способ открытия / создания файла, записи в него, а затем его закрытия? Глядя на Котлин.Ио ссылка и документация 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)
    }
}