说如果我想解析一个大文件,像这样:
val iStream = MyFile::class.java
.getResourceAsStream("largeFile.txt")
iStream.bufferedReader(Charsets.UTF_8).useLines { lines ->
lines.filterNot { it.startsWith("#") }
// parseing
.toSet()
}
但是,如果我想将largeFile拆分为多个较小的文件,如何链接序列?
例如 :
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: Stream closed
,这是合理的,因为解析是在scope
块的use
之外的。如何解决问题?
我知道可能存在一些嵌套解决方案(嵌套
useLines
...),但是我认为这很丑陋。还有其他flat
解决方案吗? 最佳答案
您可以颠倒逻辑。重要的是,一切都在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()
}
}
另一种选择是打开流,省略
use
并最终自己关闭它们,就像@MarkoTopolnik在注释中指出的那样:val inputStreams = sequenceOf("file1.txt", "file2.txt")
.map { MyFile::class.java.getResourceAsStream(it) }
inputStreams.flatMap { it.bufferedReader(Charsets.UTF_8).lineSequence() }
.filterNot { it.startsWith("#") }
.toSet()
然后使用:
inputStreams.forEach(InputStream::close) // but this will fail on the first error...
或“安全”方式:
inputStreams.forEach { try { it.close() } catch (e: Exception) { e.printStackTrace() } }