我如何写入 Kotlin 中的文件?

How do I write to a file in Kotlin?

我似乎还找不到这个问题,但是 opening/creating 文件、写入文件然后关闭它的最简单、最惯用的方法是什么?查看 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()
}

这行得通,但我想知道是否有 "proper" Kotlin 方法可以做到这一点?

我觉得大部分都还不错。我唯一不同的是使用 ReadWrite 中定义的“use”扩展来自动关闭编写器。

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

更地道一点。对于 PrintWriter,此示例:

File("somefile.txt").printWriter().use { out ->
    history.forEach {
        out.println("${it.key}, ${it.value}")
    }
}

for 循环或 forEach 取决于您的风格。没有理由使用 append(x) 因为它基本上是 write(x.toString()) 并且你已经给它一个字符串。而 println(x) 在将 null 转换为 "null" 之后基本上会执行 write(x)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}")
    }
}

这就是 Kotlin 的发展方向。改变 API 中的内容,使它们成为您想要的样子。

另一个答案对此有截然不同的口味:

其他有趣的变体让您看到 Kotlin 的强大功能:

通过创建字符串来一次性写入的快速版本:

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

一些 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)
    }
}
try{
      val fileWriter = FileWriter("test.txt", true)
      fileWriter.write(string+ "\n")
      fileWriter.close()
    } catch (exception: Exception){
        println(exception.message)
    }

至少,您可以使用:

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

FileWriter 是编写字符文件的便利 class(由 Java 提供,因此在 Kotlin 中可用)。它扩展了 Closeable,因此可以被 Kotlin 的“.use”扩展方法使用。

一旦块退出,.use 扩展方法会自动关闭调用对象,从而提供一种在文件写入后关闭文件的惯用方法。

示例很简单

val path = context!!.filesDir.absolutePath // => /data/user/0/com.example.test/files
File("$path/filename.txt").writeText("hello")