2016-02-16 19 views
27

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

Это работает, но мне было интересно, если там был «правильный» Котлин способ сделать это?

+2

Я добавил больше вариантов нового ответа для удовольствия, чтобы дать вам больше идей о том, что вы можете сделать. –

ответ

49

Немного более идиоматический. Для 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) после преобразования null в "null". И println() делает правильную строку окончания.

Если вы используете классы Kotlin 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

+1

спасибо за это и другие более «веселые» варианты! +1 – yiwei

+1

это может быть другое возможное решение: File (fileName) .writeText (Iterable .joinToString(), Charsets.UTF_8) –

+0

@IstvanNagy в зависимости от размера итерации и если вы хотите материализовать полную строку перед записью. Я добавлю это к альтернативному ответу, это красиво и коротко! –

5

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

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

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

Быстрая версия, создавая строку, чтобы написать все сразу:

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

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

0

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

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