Подтвердить что ты не робот

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

Я пока не могу найти этот вопрос, но какой самый простой, самый идиоматический способ открытия/создания файла, записи на него и последующего его закрытия? Глядя на ссылку kotlin.io и документацию по Java, мне это удалось:

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

Это работает, но мне было интересно, есть ли у него "правильный" способ Котлин?

4b9b3361

Ответ 1

Немного более идиоматично. Для PrintWriter этот пример:

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) после преобразования a null в "null". И println() заканчивается правильная строка.

Если вы используете классы data Kotlin, они уже могут быть выведены, потому что у них уже есть хороший 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, чтобы сделать их такими, как вы хотите.

Дико разные вкусы для этого - в другом ответе: fooobar.com/questions/252068/...

Ответ 2

Другие веселые вариации, чтобы вы могли видеть силу Котлина:

Быстрая версия, создавая строку для записи сразу:

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

или используйте по умолчанию дляString() для каждого элемента:

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

или используйте по умолчанию дляString() для каждого элемента:

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

если у вас уже было другое расширение toFile, вы можете переписать один вызов расширения другим:

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

Ответ 3

В основном это выглядит нормально. Единственное, что я сделал бы, это использовать расширение use", определенное в ReadWrite, для автоматического закрытия записи.

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

Ответ 4

Некоторые магии Kotlin позволяют пропустить ссылку на поток при каждом вызове на чтение или запись:

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

Ответ 5

Как минимум, вы можете использовать:

FileWriter(filename).use { it.write(text) }

FileWriter - это удобный класс для записи файлов символов (предоставляется Java и, следовательно, доступен в Kotlin). Он расширяет Closeable, и, следовательно, может использоваться методом расширения ".use" Kotlin.

Метод расширения .use автоматически закрывает вызывающий объект после выхода из блока, обеспечивая тем самым идиоматический способ закрыть файл после его записи.

Ответ 6

try{
      val fileWriter = FileWriter("test.txt", true)
      fileWriter.write(string+ "\n")
      fileWriter.close()
    } catch (exception: Exception){
        println(exception.message)
    }