Kotlin从不同的InputStream链接多个序列?

Kotlin从不同的InputStream链接多个序列?,kotlin,sequence,Kotlin,Sequence,假设我想解析这样的大文件: val iStream = MyFile::class.java .getResourceAsStream("largeFile.txt") iStream.bufferedReader(Charsets.UTF_8).useLines { lines -> lines.filterNot { it.startsWith("#") } // parseing .toSet() } 但是如果我想将大文件拆分为多个小文件,如何链接

假设我想解析这样的大文件:

val iStream = MyFile::class.java
    .getResourceAsStream("largeFile.txt")

iStream.bufferedReader(Charsets.UTF_8).useLines { lines ->
    lines.filterNot { it.startsWith("#") }
    // parseing
    .toSet()
}
但是如果我想将大文件拆分为多个小文件,如何链接序列

例如:

val seq1 = MyFile::class.java.getResourceAsStream("file1.txt")
    .use { it.bufferedReader(Charsets.UTF_8).lineSequence() }
val seq2 = MyFile::class.java.getResourceAsStream("file2.txt")
    .use { it.bufferedReader(Charsets.UTF_8).lineSequence() }

sequenceOf(seq1, seq2).flatten()
  .filterNot { it.startsWith("#") }
  // parsing
  .toSet()
它将抛出
java.io.IOException:streamclosed
,这是合理的,因为解析在
use
块的
范围之外

如何解决这个问题


我知道可能有一些嵌套解决方案(嵌套
useLines
…),但我认为这很难看。还有其他的
平面
解决方案吗?

您可以颠倒您的逻辑。重要的是,一切都要在
use
中获得或处理,否则这将不起作用,正如您已经知道的那样

一个这样的反转可能看起来像:

setOf("file1.txt", "file2.txt")
  .map { MyFile::class.java.getResourceAsStream(it) }
  .flatMap {
    it.use {
      it.bufferedReader(Charsets.UTF_8)
        .lineSequence()
        .filterNot { it.startsWith("#") }
        .toSet()
    }
  }
或者,如果您希望从外部传递链转换或过滤器,可能类似于:

val handleLine : (Sequence<String>) -> Sequence<String> = {
  it.filterNot { it.startsWith("#") }
  // .map { ... whatever }
}
setOf("file1.txt", "file2.txt")
  .map { MyFile::class.java.getResourceAsStream(it) }
  .flatMap {
    it.use {
      handleLine(it.bufferedReader(Charsets.UTF_8).lineSequence())
        .toSet()
    }
  }
然后使用:

inputStreams.forEach(InputStream::close) // but this will fail on the first error...
或“安全”方式:


我也在找同样的东西,我有很多大文件,想按顺序处理它们。您可以将文件处理包装到协同程序(例如序列生成器)中,以便在返回行时保持流

在这里,我遍历一个目录并打印所有行:

val base = File("....")

base.walkTopDown()
        .filter { it.isFile }
        .flatMap { file -> sequence {
                file.useLines { lines ->
                    lines.forEach { yield(it) }
                }
            }
        }
        .forEach { println(it) }

如果我理解你的问题,我也有类似的问题。 我必须以相同的方式处理多个文件中的每一行。我不想查看单个文件,而是将所有行作为一个序列。 以下是我的方法:

fun main(){
println(path.get(“”.toAbsolutionPath())
val filenames=listOf(“file1”、“file2”)
val行=序列{
文件名
.asSequence()
.map{LoggingReader(文件(it))}
弗雷奇先生{
it.useLines{yieldAll(it)}
}
}
lines.forEachIndexed{i,line->println($i$line”)}
}
类LoggingReader(val文件:file):BufferedReader(file.BufferedReader()){
初始化{
println(“$file-open”)
}
覆盖乐趣关闭(){
super.close()
println(“$file-closed”)
}
}
该代码给出了

file1-打开
0文件1-1
1文件1-2
2文件1-3
文件1-已关闭
文件2-打开
3文件2-1
4文件2-2
5文件2-3
文件2-已关闭
注意这一行非常重要
.asSequence()
。如果没有该行,它将处理
.foreach
之前的整个
.map
语句。这样,它将在任何读取之前打开所有文件。下面是这个案例的输出

file1-打开
文件2-打开
0文件1-1
1文件1-2
2文件1-3
文件1-已关闭
3文件2-1
4文件2-2
5文件2-3
文件2-已关闭

显然,您必须扩展资源的范围。因为您想要一个平面解决方案,所以必须选择一个覆盖这两个文件的上层作用域,或者放弃作用域资源管理,让文件保持打开状态,维护对它们的引用,并在处理完成后关闭它们。是否有这方面的代码段?如何创建一个自动关闭两个(或更多)输入流的作用域?这看起来非常好。
inputStreams.forEach { try { it.close() } catch (e: Exception) { e.printStackTrace() } }
val base = File("....")

base.walkTopDown()
        .filter { it.isFile }
        .flatMap { file -> sequence {
                file.useLines { lines ->
                    lines.forEach { yield(it) }
                }
            }
        }
        .forEach { println(it) }