任意の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
、 から への暗黙的な変換を提供ByteBuffer
しRichByteBuffer
ます)。
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
そのため、データがどのように見えるかについてより多くの知識がなければ、弱い階層として保存するだけでなく、何を推奨するかを知るのは困難です上で説明したように、バイナリ ファイルです。