2017-02-21 15 views
1

Я использую RandomAccessFile, чтобы прочитать некоторые сведения из большого файла. RandomAccessFile имеет метод seek, который указывает курсор на определенную часть файла, которую я хочу прочитать всю строку. Чтобы прочитать эту строку, я использую метод readLine().Самый быстрый способ прочитать строку в файле

Я прочитал весь этот файл до и затем создал индекс, который позволяет мне получить доступ к началу любой строки с помощью метода seek. Этот показатель отлично работает. Я создал этот индекс на основе этого ответа: https://stackoverflow.com/a/42077860/763368

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

Я прочитал, что FileChannel с MappedByteBuffer - это хороший вариант для быстрого чтения файлов, но я не видел решения, которое делает то, что я хочу.

P.S .: линии имеют разную длину, и я не знаю этой длины.

Есть ли у кого-нибудь хорошее решение?

Edit:

Файл Я хочу прочитать имеется следующий формат: значение ключа\t

Индекс является HashMap со всеми ключами этого файла было ключей и значений позиция байта (Long).

Давайте предположим, что я хочу перейти к строке с ключом «Foo», то я должен стремиться к позиции значения, как это:

raf.seek(index.get("foo")) 

Если я использую raf.readLine() возвращение будет все линия с ключом "foo".

Но я не хочу использовать RandomAccessFile для этой работы, потому что она слишком медленная.

Вот так я делаю сейчас в Scala:

val raf = new RandomAccessFile(file,"r") 
raf.seek(position.get(key)) 
println(raf.readLine) 
raf.close 
+2

Вы доступ к различным файлам? если нет, то почему вы закрываете доступ к файлу? Если вы сохраняете доступ к файлу открытым, вам не нужно ждать, пока ОС даст вам разрешение на чтение. – Tschallacka

+0

@Tschallacka Я только закрываю в конце всех чтений, это просто пример. Но моя проблема здесь в том, как читать файл. –

+0

Можете ли вы предоставить код чтения индекса и как вы переводите его в позицию поиска. Поскольку вы уже находитесь на хорошем пути, поиск индекса может выиграть от некоторой оптимизации, но без полного кода и выборочных данных это сложно. – Tschallacka

ответ

1

Если у вас уже есть, чтобы прочитать файл один раз, чтобы найти индексы ключей, абсолютно быстрым решением было бы читать строки и держать их в памяти. Если это почему-то не работает (например, ограничения памяти), использование буферов действительно может быть хорошей альтернативой. Это схема кода:

FileChannel channel = new RandomAccessFile("/some/file", "r").getChannel(); 

long pageSize = ...; // e.g. "3 GB or file size": max(channel.size(), THREE_GB); 
long position = 0; 
ByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, position, pageSize); 

ByteBuffer slice; 
int maxLineLength = 30; 
byte[] lineBuffer = new byte[maxLineLength]; 

// Read line at indices 20 - 25 
buffer.position(20); 
slice = buffer.slice(); 
slice.get(lineBuffer, 0, 6); 
System.out.println("Starting at 20:" + new String(lineBuffer, Charset.forName("UTF8"))); 

// Read line at indices 0 - 10 
buffer.position(0); 
slice = buffer.slice(); 
slice.get(lineBuffer, 0, 11); 
System.out.println("Starting at 0:" + new String(lineBuffer, Charset.forName("UTF8"))); 

Этот код также может быть использован для очень больших файлов. Просто позвоните channel.map, чтобы найти «страницу», где ключ находится: position = keyIndex/pageSize * pageSize и затем вызвать buffer.position из этого индекса: keyIndex - position

Если вы действительно не имеют какой-либо способ для группового доступа к одной «странице» вместе, то вам не нужно slice.Производительность не будет так хорошо, но это позволяет упростить код дальше:

byte[] lineBuffer = new byte[maxLineLength]; 
// ... 
ByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, keyIndex, lineLength); 
buffer .get(lineBuffer, 0, lineLength); 
System.out.println(new String(lineBuffer, Charset.forName("UTF8"))); 

Обратите внимание, что ByteBuffer не создается на JVM кучи, но на самом деле память отображается файл на уровне операционной системы. (Начиная с Java 8, вы можете проверить это, посмотрев исходный код и выполнив поиск sun.nio.ch.DirectBuffer в реализации).

размер Line: Лучший способ получить размер строки, чтобы сохранить его при сканировании через файл, то есть использовать Map[String, (Long, Int)] вместо того, что вы используете для index в настоящее время. Если это не работает для вас, вы должны выполнить некоторые тесты, чтобы выяснить, что быстрее:

  • Просто хранить размер строки максимум, а затем искать разрыв строки в строке этой максимальной длины. В этом случае обратите внимание, что вы закрываете доступ к концу файла в своих модульных тестах.
  • Сканирование вперед с ByteBuffer.get, пока вы не нажмете \n. Если у вас есть настоящие Unicode-файлы, это, вероятно, не вариант, так как код Ascii для разрыва строки (0x0A) может появиться в другом месте, например, в кодированном корейском слоге UTF-16 с кодом символа 0xAC0A.

Это будет код Scala для второго подхода:

// this happens once 
val maxLineLength: Long = 2000 // find this in your initial sequential scan 
val lineBuffer = new Array[Byte](maxLineLength.asInstanceOf[Int]) 

// this is how you read a key 
val bufferLength = maxLineLength min (channel.size() - index("key")) 
val buffer = channel.map(FileChannel.MapMode.READ_ONLY, index("key"), bufferLength) 
var lineLength = 0 // or minLineLength 
while (buffer.get(lineLength) != '\n') { 
    lineLength += 1 
} 
buffer.get(lineBuffer, 0, lineLength - 1) 
println(new String(lineBuffer, Charset.forName("UTF8"))) 
+0

У меня есть индекс, поэтому я могу получить доступ к началу строки. Я обращаюсь к этому индексу, а затем ищем там. С другими параметрами, отличными от RandomAccessFile, я бы тоже хотел найти эту позицию, индекс также будет использоваться. –

+0

Я прочитал весь файл до и затем создал индекс. Я помещаю thisindex в память, поэтому я могу получить доступ к этому и перейти к началу строки с поиском mothod. С другими вариантами, отличными от RandomAccessFile, я бы тоже хотел найти эту позицию, индекс будет использоваться слишком –

+0

Нет, я не могу поместить этот файл в память, это более 100 ГБ. Мое решение работает, но оно медленное, и это моя проблема. –