Gzip для чтения файлов Scala
Я новичок в скале и выясняю вещи на лету. У меня есть программа, которая должна читать файлы Gzip разных размеров - 20 КБ, 2 МБ и 150 МБ (Да, сжатый файл имеет размер 150 МБ). Я бы подумал не о том, чтобы по-разному читать разные файлы, а о стандартном сквозном. Большинство подходов, которые я вижу, используют размер буфера 64 МБ для чтения файлов построчно? Каков наилучший (читай как, *самая быстрая и чистая память * способ сделать это) способ сделать это?
Заранее спасибо за помощь!
обновление 1:
Большие улучшения в скорости чтения.(Я бы даже поделился своими очками кармы) Спасибо ТАК!:)
Но я заметил, что, поскольку каждый мой файл имеет около 10 тыс. Строк, при записи их в файл требуется много времени, чтобы преобразовать String Iterator в строку перед записью в файл. Я могу сделать два подхода,
- Итератор построчно и запись построчно в файл.
- Повторяйте строку за строкой, чтобы преобразовать строки в большую строку ("\n" с разделителями) и записать эту большую строку в файл.
Я предполагаю, что [2] будет быстрее. Итак, это то, что я делаю для письма,
var processedLines = linesFromGzip(new File(fileName)).map(line => MyFunction(line))
var outFile = Resource.fromFile(outFileName)
outFile.write(processedLines.mkString("\n")) // severe overhead -> processedLines.mkString("\n")
Кроме того, мой анализ (комментируя write() показывает, что для написания требуется не много времени, а для преобразования processedLines
на одну большую строку - это занимает около секунды - это огромные затраты для моего приложения. Какой самый лучший (снова чистый без утечек памяти) способ сделать это.
1 ответ
Ваша проблема с памятью вызвана слишком большим количеством открытых файлов, а не размером файлов. Вам нужен механизм для автоматического закрытия каждого файла после прочтения.
Один из способов сделать это:
// this Source closes at the end of iteration
implicit def closingSource(source: Source) = new {
val lines = source.getLines()
var isOpen = true
def closeAfterGetLines() = new Iterator[String] {
def hasNext = isOpen && hasNextAndCloseIfDone
def next() = {
val line = lines.next()
hasNextAndCloseIfDone
line
}
private def hasNextAndCloseIfDone = if (lines.hasNext) true else { source.close() ; isOpen = false ; false }
}
}
а затем вы используете читателя gzip:
def gzInputStream(gzipFile: File) = new GZIPInputStream(new BufferedInputStream(new FileInputStream(gzipFile)))
def linesFomGzip(gzipFile: File): Iterator[String] = {
Source.fromInputStream(gzInputStream(gzipFile)).closeAfterGetLines()
}
Обратите внимание, что файлы закрываются, только если итерация завершена, то есть весь файл читается. Если (по какой-то причине) вы не прочитали весь файл, вам необходимо вручную закрыть файл.