如何在Scala中将文件作为字节数组读取
Posted
技术标签:
【中文标题】如何在Scala中将文件作为字节数组读取【英文标题】:How to read a file as a byte array in Scala 【发布时间】:2011-11-27 17:28:16 【问题描述】:我可以找到大量示例,但它们似乎要么主要依赖于 Java 库,要么只是读取字符/行/等。
我只想读入一些文件并获得一个带有 scala 库的字节数组 - 有人可以帮我吗?
【问题讨论】:
我认为依赖 Java 库是(几乎?)每个人都会做的事情,包括 Scala 库。参见例如 scala.io.Source 的源代码。 您使用的不是其他语言,只是一个标准的 JVM API,它已被证明足够好,不需要替换! 那么,您认为Java 类是如何实现的?在内心深处,有一个本地方法:它只有一个签名,没有 Java 实现,并且依赖于特定于操作系统的 C 实现。这不也是作弊吗? :) 应该说.Net上的Scala确实让这个问题变得更加紧迫。 @Philippe:当然,使用 C 只是在汇编上作弊:P...我的意思是,语言之间的边界通常是相当明确的,Scala 和 Java 有点融为一体彼此。 【参考方案1】:Java 7:
import java.nio.file.Files, Paths
val byteArray = Files.readAllBytes(Paths.get("/path/to/file"))
我相信这是最简单的方法。只是在这里利用现有的工具。 NIO.2 很棒。
【讨论】:
我认为没有绑定到 jvm 【参考方案2】:这应该可以工作(Scala 2.8):
val bis = new BufferedInputStream(new FileInputStream(fileName))
val bArray = Stream.continually(bis.read).takeWhile(-1 !=).map(_.toByte).toArray
【讨论】:
我认为这是包装 Java API 函数以获取 Stream 语义的一个很好的例子。非常感谢。val bis = new java.io.BufferedInputStream(new java.io.FileInputStream(fileName));
如果您没有导入 java 路径
使用这种方法,是也需要关闭文件还是隐式的?
你需要自己关闭
这种方法很慢,因为它需要处理每个字节。理想情况下,I/O 操作应该是基于块的。【参考方案3】:
库scala.io.Source有问题,不要用它来读取二进制文件。
可以按照此处的说明重现错误:https://github.com/liufengyun/scala-bug
在data.bin
文件中,包含十六进制的0xea
,即二进制的11101010
,需要转换成十进制的234
。
main.scala
文件包含两种读取文件的方式:
import scala.io._
import java.io._
object Main
def main(args: Array[String])
val ss = Source.fromFile("data.bin")
println("Scala:" + ss.next.toInt)
ss.close
val bis = new BufferedInputStream(new FileInputStream("data.bin"))
println("Java:" + bis.read)
bis.close
当我运行scala main.scala
时,程序输出如下:
Scala:205
Java:234
Java 库生成正确的输出,而 Scala 库没有。
【讨论】:
如果我将编码设置为Source.fromFile("data.bin", "ISO8859-1")
,效果很好。
也许有用,但实际上,这不是答案。在答案中引入新问题是不具建设性的,属于其他地方。【参考方案4】:
val is = new FileInputStream(fileName)
val cnt = is.available
val bytes = Array.ofDim[Byte](cnt)
is.read(bytes)
is.close()
【讨论】:
这不是一个有效的解决方案。来自 InputStream.available 的 javadoc:Note that while some implementations of InputStream will return the total number of bytes in the stream, many will not. It is never correct to use the return value of this method to allocate a buffer intended to hold all data in this stream.
【参考方案5】:
您也可以考虑使用scalax.io:
scalax.io.Resource.fromFile(fileName).byteArray
【讨论】:
注意到该存储库的最后一次操作是 6 年前 - 它仍然相关吗?【参考方案6】:您可以使用Apache Commons CompressIOUtils
import org.apache.commons.compress.utils.IOUtils
val file = new File("data.bin")
IOUtils.toByteArray(new FileInputStream(file))
【讨论】:
我不得不导入 import org.apache.commons.io.IOUtils 而不是建议的导入。【参考方案7】:使用 Scala Future 和 Java NIO2 进行异步文件读取
def readFile(path: Path)(implicit ec: ExecutionContext): Future[Array[Byte]] =
val p = Promise[Array[Byte]]()
try
val channel = AsynchronousFileChannel.open(path, StandardOpenOption.READ)
val buffer = ByteBuffer.allocate(channel.size().toInt);
channel.read(buffer, 0L, buffer, onComplete(channel, p))
catch
case t: Exception => p.failure(t)
p.future
private def onComplete(channel: AsynchronousFileChannel, p: Promise[Array[Byte]]) =
new CompletionHandler[Integer, ByteBuffer]()
def completed(res: Integer, buffer: ByteBuffer): Unit =
p.complete(Try
buffer.array()
)
def failed(t: Throwable, buffer: ByteBuffer): Unit =
p.failure(t)
【讨论】:
【参考方案8】:我已使用以下代码读取 CSV 文件。
import scala.io.StdIn.readLine
import scala.io.Source.fromFile
readFile("C:/users/xxxx/Downloads/", "39025968_ccccc_1009.csv")
def readFile(loc :String,filenm :String): Unit =
var flnm = fromFile(s"$loc$filenm") // Imported fromFile package
println("Files testing")
/*for (line <- flnm.getLines())
printf("%4d %s\n", line.length, line)
*/
flnm.getLines().foreach(println) // getLines() is imported from readLines.
flnm.close()
【讨论】:
对于这么老的问题(9 年前提出的问题),并且已经提交了这么多答案,指出您的新答案与以前的答案有何不同是很有帮助的。 (包括被注释掉的代码看起来很草率。) 是的.. 其他答案清楚地显示了返回的字节数组。这个真的不清楚以上是关于如何在Scala中将文件作为字节数组读取的主要内容,如果未能解决你的问题,请参考以下文章