Scala:输入流到数组 [字节]

Posted

技术标签:

【中文标题】Scala:输入流到数组 [字节]【英文标题】:Scala: InputStream to Array[Byte] 【发布时间】:2011-06-21 18:45:12 【问题描述】:

对于 Scala,从 InputStream 读取到字节数组的最佳方法是什么?

我可以看到您可以将 InputStream 转换为 char 数组

Source.fromInputStream(is).toArray()

【问题讨论】:

【参考方案1】:

怎么样:

Stream.continually(is.read).takeWhile(_ != -1).map(_.toByte).toArray

更新:使用 LazyList 代替 Stream(因为 Stream 在 Scala 3 中已弃用)

LazyList.continually(is.read).takeWhile(_ != -1).map(_.toByte).toArray

【讨论】:

你能解释一下这个和问题中的变体之间的区别吗? @Jus12 我正在寻找一个字节数组。我的问题是一种获取 char 数组的方法。 不会创建一个巨大的链表,然后将其转换为数组吗?在时间或内存方面,这看起来效率不高。 看起来这毕竟没有创建一个链表。 Stream.continually 产生一个迭代器,takeWhilemap 似乎将迭代器转换为迭代器。例如。在 Scala 2.9.3 REPL 中评估 Array(1, 2, 3, 4, -1).iterator.takeWhile(-1 !=).map(_.toByte) 给我 Iterator[Byte] = non-empty iterator 这似乎对我造成了 OOM 错误。事情最终被 GC 处理了,但峰值超出了我的服务器可以处理的范围。【参考方案2】:

刚刚通过替换消除了我们服务器代码中的瓶颈

Stream.continually(request.getInputStream.read()).takeWhile(_ != -1).map(_.toByte).toArray

org.apache.commons.io.IOUtils.toByteArray(request.getInputStream)

或者在纯 Scala 中:

def bytes(in: InputStream, initSize: Int = 8192): Array[Byte] = 
  var buf = new Array[Byte](initSize)
  val step = initSize
  var pos, n = 0
  while (
    if (pos + step > buf.length) buf = util.Arrays.copyOf(buf, buf.length << 1)
    n = in.read(buf, pos, step)
    n != -1
  ) pos += n
  if (pos != buf.length) buf = util.Arrays.copyOf(buf, pos)
  buf

在任何情况下都不要忘记关闭打开的输入流:

val in = request.getInputStream
try bytes(in) finally in.close()

【讨论】:

那是 org.apache.commons.io.IOUtils.toByteArray,以防有人想知道。 这肯定感觉更快。有人对较大的文件进行过任何基准测试或测试吗? 谢谢。使用 Apache Spark 运行它时,我遇到了 GC Overhead 错误的巨大问题,我的任务有 90% 的时间都花在了 GC 上。替换为toByteArray 大大加快了速度。 重要的是要指出这个解决方案如何真正大大超越替代方案,你有像 map(_.toByte) 这样的东西逐字节迭代输入......你正在做这个吗大数据!【参考方案3】:

与 Eastsun 的回答类似...我一开始是评论,但最终变得有点长!

我建议不要使用Stream,如果持有对头元素的引用,那么流很容易消耗大量内存。

鉴于您只会在文件中读取一次,那么Iterator 是一个更好的选择:

def inputStreamToByteArray(is: InputStream): Array[Byte] =
  Iterator continually is.read takeWhile (-1 !=) map (_.toByte) toArray

【讨论】:

【参考方案4】:
import scala.tools.nsc.io.Streamable
Streamable.bytes(is)

不记得最近的时间:可能以天为单位。回到2.8,更像

new Streamable.Bytes  def inputStream() = is  toByteArray

【讨论】:

使用 scala.tools 包中的东西安全吗?它们甚至是标准库的一部分吗? 没有。但是,如果您想知道如何编写它,就在这里。 它现在似乎已经转移到更标准的scala.reflect.io 包。 scala.reflect.io.Streamable.bytes【参考方案5】:

使用Scala IO,这应该可以:

def inputStreamToByteArray(is: InputStream): Array[Byte] = 
   Resource.fromInputStream(in).byteArray

【讨论】:

【参考方案6】:

使用better-files,您可以简单地使用is.bytes

【讨论】:

better.files 应该只在标准库中。它好多了。此外,如果您想要Array[Byte],则需要使用is.byteArray【参考方案7】:

Source.fromInputStream(is).map(_.toByte).toArray

【讨论】:

这在二进制/错误编码的文本文件上失败:***.com/questions/13327536/…【参考方案8】:

基于流和 ByteArraOutputStream 的缓冲版本解决方案如何最大限度地减少最终数组增长的样板?

val EOF: Int = -1

def readBytes(is: InputStream, bufferSize: Int): Array[Byte] = 
  val buf = Array.ofDim[Byte](bufferSize)
  val out = new ByteArrayOutputStream(bufferSize)

  Stream.continually(is.read(buf)) takeWhile  _ != EOF  foreach  n =>
    out.write(buf, 0, n)
  

  out.toByteArray

【讨论】:

【参考方案9】:

这是一种使用 scalaz-stream 的方法:

import scalaz.concurrent.Task
import scalaz.stream._
import scodec.bits.ByteVector

def allBytesR(is: InputStream): Process[Task, ByteVector] =
  io.chunkR(is).evalMap(_(4096)).reduce(_ ++ _).lastOr(ByteVector.empty)

【讨论】:

可能没有理由减少,这会破坏流的增量特性 原因是题目要求的是字节数组。【参考方案10】:

我们可以使用 Google API ByteStreams

com.google.common.io.ByteStreams

将流传递给 ByteStreams.toByteArray 方法进行转换

ByteStreams.toByteArray(stream)

【讨论】:

【参考方案11】:
def inputStreamToByteArray(is: InputStream): Array[Byte] = 
    val buf = ListBuffer[Byte]()
    var b = is.read()
    while (b != -1) 
        buf.append(b.byteValue)
        b = is.read()
    
    buf.toArray

【讨论】:

List[Byte] 有方法“add”吗?

以上是关于Scala:输入流到数组 [字节]的主要内容,如果未能解决你的问题,请参考以下文章

java中有效的输入流到字符串方法

将字节数组输入流拷贝成字节数组输出流,将ByteArrayInputStream转成ByteArrayOutputStream

UWP流到浮点数组c#

将固定字节从输入流存储到字节数组中

Java题目:编写程序,将一个字符串转为字节数组输入流。转换为大写字母输出。

字节输入流----ByteArrayInputStream