1. 概述

在本教程中,我们将探讨如何在 Scala 中将二进制输入流转换为文本数据。Scala 底层使用的是 Java 标准库中的 java.io 包中的类,但它也提供了一些更简洁的工具来简化这一过程。

其中,**scala.io.Source 类提供了便捷的方式将 java.io.InputStream 实例转换为字符串数据**,我们也会重点介绍它的使用方式。

2. 创建 Source 实例

Source 伴生对象提供了一系列工厂方法,用于创建对应不同 InputStream 实现的 Source 实例。我们来看几个常见的用法。

2.1 从 URL 读取远程数据

使用 fromURL 方法可以读取远程数据:

lazy val sourceFromUrl: Source = Source.fromURL("https://google.com")

这个例子中我们获取了 Google 首页的内容,实际中可以用于调用 API 获取 JSON 数据等。

2.2 从 classpath 读取资源文件

使用 fromResource 方法可以从 classpath 中读取资源文件:

lazy val sourceFromClassPath: Source = Source.fromResource("com.baeldung.scala.io/file_in_classpath.txt")

这个方法常用于读取测试资源文件,比如放在 src/test/resources 中的数据。

2.3 从本地文件读取内容

使用 fromFile 方法可以直接读取本地文件内容:

lazy val sourceFromFile: Source = Source.fromFile("./some_text_file")
lazy val sourceFromFileWithCustomEncoder: Source = Source.fromFile("./some_text_file", enc = "Cp1252")

第一个版本默认使用 UTF-8 编码,第二个版本允许我们指定自定义编码,比如 Cp1252

除了以上几种方式,Source 还提供了其他一些工厂方法,更多细节可以查看 Source 的源码

3. 处理 Source 中的字符串数据

获取 Source 实例后,我们可以使用以下几种方式处理数据。

3.1 使用 mkString 一次性读取全部内容

val oneLineSource = Source.fromResource("com.baeldung.scala.io/one_line_string.txt")
try {
  oneLineSource.mkString shouldEqual "One line string"
} finally {
  oneLineSource.close()
}

✅ 这种方式适合小文件,一次性读入内存。

3.2 使用 getLines 逐行处理

val fourLinesSource = Source.fromResource("com.baeldung.scala.io/four_lines_string.txt")
try {
  fourLinesSource.getLines().foreach(line => assert(line.startsWith("String")))
} finally {
  fourLinesSource.close()
}

✅ 适合逐行处理,尤其在处理结构化文本时非常实用。

⚠️ 注意:无论哪种方式,都需要手动调用 close() 方法释放资源,防止文件句柄或网络连接泄露。

4. 处理大文件或无限流

并不是所有数据都能一次性加载到内存中。对于大文件或无限流,我们需要更谨慎地处理。

好消息是,Source 提供的方法是内存安全的。比如 getLines() 返回的是一个 Iterator[String],可以按需读取,不会一次性加载全部内容。

此外,Source 本身也实现了 Iterator[Char],因此我们可以使用迭代器的方法来处理无限流:

val infiniteSource = Source.fromIterable {
  new Iterable[Char] {
    override def iterator: Iterator[Char] = Iterator.continually('A')
  }
}
infiniteSource.slice(100000, 100010).mkString shouldEqual "AAAAAAAAAA"

虽然这个例子是人为构造的,但它展示了如何处理像基因组数据这样的大规模数据流。

5. 总结

在这篇文章中,我们介绍了 scala.io.Source 类的一些核心功能,展示了如何在 Scala 中将各种 InputStream 数据源转换为文本数据。

总的来说,Source 类提供了简单、直观且安全的 API,适合大多数 I/O 场景。

📦 所有示例代码均可在 GitHub 仓库 中找到。


原始标题:Nested forEach in Kotlin