2015-10-23 5 views
0

Я пытаюсь прочитать двоичный файл (16 МБ), в котором у меня есть только целые числа, закодированные на 16 бит. Поэтому для этого я использовал куски размером 1 МБ, которые дают мне массив байтов. Для моих собственных нужд я преобразовываю этот массив байтов в короткий массив со следующей функцией convert, но чтение этого файла с помощью буфера и преобразование его в короткий массив займет у меня 5 секунд, это быстрее, чем мое решение?Эффективный способ чтения двоичных файлов в scala

def convert(in: Array[Byte]): Array[Short] = in.grouped(2).map { 
    case Array(one) => (one << 8 | (0 toByte)).toShort 
    case Array(hi, lo) => (hi << 8 | lo).toShort 
    } .toArray 

    val startTime = System.nanoTime() 

val file = new RandomAccessFile("foo","r") 
val defaultBlockSize = 1 * 1024 * 1024 
    val byteBuffer = new Array[Byte](defaultBlockSize) 
    val chunkNums = (file.length/defaultBlockSize).toInt 
    for (i <- 1 to chunkNums) { 
     val seek = (i - 1) * defaultBlockSize 
     file.seek(seek) 
     file.read(byteBuffer) 
     val s = convert(byteBuffer) 
     println(byteBuffer size) 
    } 

val stopTime = System.nanoTime() 
    println("Perf of = " + ((stopTime - startTime)/1000000000.0) + " for a duration of " + duration + " s") 
+0

Er, в каком десятилетии 16 мегабайт «огромный»? Накладные расходы JVM обычно составляют 10 или более раз. –

+0

Возможно, это потому, что я впервые читал такие файлы, но я удалил «огромный». – alifirat

ответ

2

16 МБ легко помещается в память, если вы не используете это на телефоне или что-то в этом роде. Не нужно обрывать его и делать логику сложнее.

Просто проглатывать весь файл сразу с java.nio.files.Files.readAllBytes:

val buffer = java.nio.files.Files.readAllBytes(myfile.toPath) 

если вы не застряли с Java 1.6. (Если вы застряли с Java 1.6, предварительно выделить размер буфера с помощью myfile.size и использовать read на FileInputStream, чтобы получить все это на одном дыхании. Это не намного сложнее, просто не забудьте закрыть его!)

Тогда, если вы не хотите, чтобы преобразовать его самостоятельно, вы можете

val bb = java.nio.ByteBuffer.wrap(buffer) 
bb.order(java.nio.ByteOrder.nativeOrder) 
val shorts = new Array[Short](buffer.length/2) 
bb.asShortBuffer.get(shorts) 

И вы сделали.

Обратите внимание, что это все Java-материал; здесь нет ничего специфичного для Scala сохранения синтаксиса.

Если вы задаетесь вопросом, почему это так гораздо быстрее, чем ваш код, это потому, что grouped(2) коробки байтов и помещает их в массив. Это три ассигнований на каждый короткий вы хотите! Вы можете сделать это самостоятельно, указав массив напрямую, и это будет быстро, но почему вы хотите, чтобы ByteBuffer и друзья сделали именно то, что вам нужно?

Если вы действительно действительно заботы о том, что последних (нечетных) байтах, то вы можете использовать (buffer.length + 1)/2 для размера shorts и липкости на if ((buffer.length) & 1 == 1) shorts(shorts.length-1) = ((bb.get&0xFF) << 8).toShort, чтобы захватить последние байты.

+0

Благодарим вас за объяснение, а также за последний байт. Мне нужно получить все значения из файла, поэтому для меня также очень важен последний (нечетный) байт :) – alifirat

+0

@alifirat - это очень странная схема кодирования, в которой вы оставляете последний байтовый короткий текст при записи на диск ! –

0

Несколько вопросов выскочить:

Если byteBuffer всегда будет 1024 * 1024 Размер тогда фактически никогда не будет использоваться case Array(one) в convert и поэтому сопоставление с образцом ненужными.

Кроме того, вы можете избежать цикла for с хвостовой рекурсивной функцией. После val byteBuffer = ... линии вы можете заменить chunkNums и цикл с:

@scala.annotation.tailrec 
def readAndConvert(b: List[Array[Short]], file : RandomAccessFile) : List[Array[Short]] = { 
    if(file.read(byteBuffer) < 0) 
    b 
    else { 
    file.skipBytes(1024*1024) 
    readAndConvert(b.+:(convert(byteBuffer)), file) 
    } 
} 

val sValues = readAndConvert(List.empty[Array[Short]], file) 

Примечание: потому что список preppending гораздо быстрее, чем прилагая выше цикл получает вас преобразованное значение в обратном порядке от порядка чтения в файле.

+0

Ни одна из них не является реальной проблемой. –

+0

Я не согласен, поиск намного дороже, чем пропустить, а цикл for, используемый в вопросе, не оптимизирован. –

Смежные вопросы