3

したがって、 のペアを最大 10000 のサイズの に関連付ける関連付けがありIntVector[Long]そのようなデータが数十万から 100 万あります。これを単一のファイルに保存して、後で Scala で処理したいと考えています。

明らかにこれをプレーンテキスト形式で保存するとスペースがかかりすぎるため、Byteストリームを作成してそれを行う方法を見つけようとしています。byteValue()ただし、 aがまだ 4 バイトの長さLongの表現を返すように思われるため、これが機能するかどうかはよくわかりません。したがって、スペースを節約できませんか? Byteバイナリ形式を扱った経験はあまりありません。

Scala 標準ライブラリには、BytePickle私が探していたものと思われるものがあったようですが、その後非推奨になりましたか?

4

3 に答える 3

7

任意のLong長さは約 19.5 ASCII 桁ですが、長さは 8 バイトしかないため、バイナリで記述すると最大 2 倍の節約になります。現在、ほとんどの値が実際には 8 バイトすべてを使用していない可能性があります。その場合、圧縮方式を自分で定義できます。

いずれにせよ、ブロック データは and フレンドを使用して書き込むことをお勧めしますjava.nio.ByteBuffer。バイナリ データはブロック単位で最も効率的に読み取られるため、ファイルをランダムにアクセスできるようにしたい場合があります。その場合、データを次のように表示する必要があります。

<some unique binary header that lets you check the file type>
<int saying how many records you have>
<offset of the first record>
<offset of the second record>
...
<offset of the last record>
<int><int><length of vector><long><long>...<long>
<int><int><length of vector><long><long>...<long>
...
<int><int><length of vector><long><long>...<long>

ByteBufferこれは、すべての大きさが事前にわかっているため、読み書きに特に便利な形式です。だからあなたはできる

val fos = new FileOutputStream(myFileName)
val fc = fos.getChannel // java.nio.channel.FileChannel
val header = ByteBuffer.allocate(28)
header.put("This is my cool header!!".getBytes)
header.putInt(data.length)
fc.write(header)
val offsets = ByteBuffer.allocate(8*data.length)
data.foldLeft(28L+8*data.length){ (n,d) =>
  offsets.putLong(n)
  n = n + 12 + d.vector.length*8
}
fc.write(offsets)
...

そして帰る途中

val fis = new FileInputStream(myFileName)
val fc = fis.getChannel
val header = ByteBuffer.allocate(28)
fc.read(header)
val hbytes = new Array[Byte](24)
header.get(hbytes)
if (new String(hbytes) != "This is my cool header!!") ???
val nrec = header.getInt
val offsets = ByteBuffer.allocate(8*nrec)
fc.read(offsets)
val offsetArray = offsets.getLongs(nrec)  // See below!
...

には存在しない便利なメソッドがいくつかありByteBufferますが、それらを暗黙的に追加できます (ここでは Scala 2.10 の場合。2.9 ではそれを単純なクラスにし、 を削除してextends AnyVal、 から への暗黙的な変換を提供ByteBufferRichByteBufferます)。

implicit class RichByteBuffer(val b: java.nio.ByteBuffer) extends AnyVal {
  def getBytes(n: Int) = { val a = new Array[Byte](n); b.get(a); a }
  def getShorts(n: Int) = { val a = new Array[Short](n); var i=0; while (i<n) { a(i)=b.getShort(); i+=1 } ; a }
  def getInts(n: Int) = { val a = new Array[Int](n); var i=0; while (i<n) { a(i)=b.getInt(); i+=1 } ; a }
  def getLongs(n: Int) = { val a = new Array[Long](n); var i=0; while (i<n) { a(i)=b.getLong(); i+=1 } ; a }
  def getFloats(n: Int) = { val a = new Array[Float](n); var i=0; while (i<n) { a(i)=b.getFloat(); i+=1 } ; a }
  def getDoubles(n: Int) = { val a = new Array[Double](n); var i=0; while (i<n) { a(i)=b.getDouble(); i+=1 } ; a }
}

とにかく、このようにする理由は、まともなパフォーマンスが得られることです。これは、数十ギガバイトのデータがある場合の考慮事項でもあります (これは、最大数十万のベクトルの長さを指定したように聞こえます)。万)。

問題が実際にははるかに小さい場合は、それほど心配する必要はありません。XML にパックするか、JSON またはカスタム テキスト ソリューションを使用DataOutputStreamします (または、 andを使用しDataInputStreamます。アクセス)。

問題が実際にはもっと大きい場合は、long の2 つのリストを定義できます。最初に、たとえば、に収まるものInt、次に実際にフルが必要なものLong(インデックスを使用して、それらがどこにあるかがわかります)。データ圧縮は非常にケース固有のタスクです--使用したいだけではないと仮定すると--java.util.zipそのため、データがどのように見えるかについてより多くの知識がなければ、弱い階層として保存するだけでなく、何を推奨するかを知るのは困難です上で説明したように、バイナリ ファイルです。

于 2013-01-04T00:03:21.560 に答える
5

Java の を参照してくださいDataOutputStream。これにより、プリミティブ型とStringsバイト ストリームを簡単かつ効率的に書き込むことができます。特に、次のようなものが必要です。

val stream = new DataOutputStream(new FileOutputStream("your_file.bin"))

その後、同等のDataInputStreamメソッドを使用して、そのファイルから変数を再度読み取ることができます。

于 2013-01-03T23:31:08.170 に答える
2

を使用してscala-io、-sscala-armのバイナリ ストリームを記述しました。Longライブラリ自体は、物事を行うための Scala の方法であると想定されていますが、これらは Scalamasterブランチにはありません - 誰かが理由を知っているのでしょうか? 私は時々それらを使用します。

1) クローンscala-io:

git clone https://github.com/scala-incubator/scala-io.git

に行き、あなたのものscala-io/packageに着替えますBuild.scalaval scalaVersion

sbt package

2) クローンscala-arm:

git clone https://github.com/jsuereth/scala-arm.git

に行き、あなたのものscala-arm/packageに着替えますbuild.scalascalaVersion :=

sbt package

3) それほど遠くない場所にコピーします。

scala-io/core/target/scala-xxx/scala-io-core_xxx-0.5.0-SNAPSHOT.jar

scala-io/file/target/scala-xxx/scala-io-file_xxx-0.5.0-SNAPSHOT.jar

scala-arm/target/scala-xxx/scala-arm_xxx-1.3-SNAPSHOT.jar

4) REPL を開始します。 scala -classpath "/opt/scala-io/scala-io-core_2.10-0.5.0-SNAPSHOT.jar: /opt/scala-io/scala-io-file_2.10-0.5.0-SNAPSHOT.jar: /opt/scala-arm/scala-arm_2.10-1.3-SNAPSHOT.jar"

5):paste実際のコード:

import scalax.io._

// create data stream
val EOData = Vector(0xffffffffffffffffL)
val data = List(
  (0, Vector(0L,1L,2L,3L))
  ,(1, Vector(4L,5L))
  ,(2, Vector(6L,7L,8L))
  ,(3, Vector(9L))  
)
var it = Iterator[Long]()
for (rec <- data) {
  it = it ++ Vector(rec._1).iterator.map(_.toLong)
  it = it ++ rec._2.iterator
  it = it ++ EOData.iterator
}

// write data at once
val out: Output = Resource.fromFile("/tmp/data")
out.write(it)(OutputConverter.TraversableLongConverter)  
于 2013-01-04T00:38:47.897 に答える