Чтение строк символов и получение позиции файла
Я читаю последовательные строки символов из текстового файла. Кодирование символов в файле может быть не однобайтным.
В определенные моменты я хотел бы получить позицию файла, в которой начинается следующая строка, чтобы я мог повторно открыть файл позже и быстро вернуться в эту позицию.
Вопросы
Есть ли простой способ сделать это, желательно, используя стандартные библиотеки Java?
Если нет, то что разумное обходное решение?
Атрибуты идеального решения
Идеальное решение будет обрабатывать множественные кодировки символов. Это включает в себя UTF-8, в котором разные символы могут быть представлены различными байтами. Идеальное решение будет в основном полагаться на надежную, хорошо поддерживаемую библиотеку. Наиболее идеальным будет стандартная библиотека Java. Лучше всего было бы использовать Apache или Google. Решение должно быть масштабируемым. Чтение всего файла в память не является решением. Возврат в позицию не требует считывания всех предыдущих символов в линейном времени.
Подробнее
Для первого требования BufferedReader.readLine()
является привлекательным. Но буферизация явно препятствует получению значимой позиции файла.
Менее очевидно, что InputStreamReader
также может читать вперед, мешая получению позиции файла. Из Документация InputStreamReader:
Чтобы включить эффективное преобразование байтов в символы, большее количество байтов может быть прочитано впереди от базового потока, чем необходимо для выполнения текущей операции чтения.
Метод RandomAccessFile.readLine()
читает один байт на символ.
Каждый байт преобразуется в символ, беря байтовое значение для младших восьми бит символа и устанавливая высокие восемь бит символа равным нулю. Поэтому этот метод не поддерживает полный набор символов Unicode.
Ответы
Ответ 1
Если вы построите BufferedReader
из FileReader
и сохраните экземпляр FileReader
, доступный для вашего кода, вы сможете получить позицию следующей строки, вызвав:
fileReader.getChannel().position();
после вызова bufferedReader.readLine()
.
BufferedReader
может быть сконструирован с входным буфером размера 1, если вы готовы торговать приростом производительности для позиционной точности.
Альтернативное решение
Что было бы неправильно с отслеживанием самих байтов:
long startingPoint = 0; // or starting position if this file has been previously processed
while (readingLines) {
String line = bufferedReader.readLine();
startingPoint += line.getBytes().length;
}
это даст вам количество байтов с точностью до того, что вы уже обработали, независимо от базовой маркировки или буферизации. Вам придется учитывать окончание строк в вашей таблице, так как они лишены.
Ответ 2
Случай, похоже, решается VTD-XML, библиотекой, способной быстро анализировать большие XML файлы:
Последняя реализация java VTD-XML ximpleware, в настоящее время 2.13 http://sourceforge.net/projects/vtd-xml/files/vtd-xml/ предоставляет некоторый код, поддерживающий смещение байта после каждого вызова getChar ( ) метода его реализации IReader.
Реализации IReader для различных кодировок caracter доступны внутри VTDGen.java и VTDGenHuge.java
Реализации IReader предусмотрены для следующих кодировок
ASCII;
iso_8859_1
ISO_8859_10
ISO_8859_11
ISO_8859_12
ISO_8859_13
ISO_8859_14
ISO_8859_15
ISO_8859_16
ISO_8859_2
ISO_8859_3
ISO_8859_4
ISO_8859_5
ISO_8859_6
ISO_8859_7
ISO_8859_8
ISO_8859_9
UTF_16BE
UTF_16LE
UTF8;
WIN_1250
WIN_1251
WIN_1252
WIN_1253
WIN_1254
WIN_1255
WIN_1256
WIN_1257
WIN_1258
Ответ 3
Я бы предложил java.io.LineNumberReader
. Вы можете установить и получить номер строки и, следовательно, продолжить с определенного индекса строки.
Так как это BufferedReader
, он также способен обрабатывать UTF-8.
Ответ 4
Решение A
Проблема с чем-либо еще заключается в том, что вам придется абсолютно убедиться, что вы никогда не читали символ EOL.
readChar() возвращает char не байт. Поэтому вам не нужно беспокоиться о ширине символов.
Читает символ из этого файла. Этот метод считывает два байта из файла, начиная с текущего указателя файла.
[...]
Этот метод блокируется до тех пор, пока не будут прочитаны два байта, не будет обнаружен конец потока или исключено исключение.
Используя RandomAccessFile, а не Reader, вы отказываетесь от способности Java расшифровывать кодировку в файле для вас. BufferedReader сделает это автоматически.
Существует несколько способов преодоления этого. Один заключается в том, чтобы самостоятельно определить кодировку, а затем использовать правильный метод read *(). Другой способ - использовать поток BoundedInput.
В этом вопросе есть один Java: чтение строк из файла произвольного доступа с буферизованным вводом
например. fooobar.com/questions/333226/...
Ответ 5
RandomAccessFile имеет функцию:
искать (long pos) Устанавливает смещение указателя файла, измеренное от начала этого файла, при котором происходит следующее чтение или запись.
Ответ 6
В этом частичном обходном пути рассматриваются только файлы, закодированные с 7-битным ASCII или UTF-8. Ответ с общим решением все же желателен (как и критика этого обходного пути).
В UTF-8:
- Все однобайтовые символы можно отличить от всех байтов в многобайтовых символах. Все байты в многобайтовом символе имеют "1" в позиции высокого порядка. В частности, байты, представляющие LF и CR, не могут быть частью многобайтового символа.
- Все однобайтовые символы находятся в 7-битном ASCII. Таким образом, мы можем декодировать файл, содержащий только 7-битные символы ASCII с декодером UTF-8.
Взятые вместе, эти две точки означают, что мы можем читать строку с чем-то, что считывает байты, а не символы, а затем декодирует строку.
Чтобы избежать проблем с буферизацией, мы можем использовать RandomAccessFile
. Этот класс предоставляет методы для чтения строки и получает/устанавливает положение файла.
Здесь представлен эскиз кода для чтения следующей строки как UTF-8 с использованием RandomAccessFile.
protected static String
readNextLineAsUTF8( RandomAccessFile in ) throws IOException {
String rv = null;
String lineBytes = in.readLine();
if ( null != lineBytes ) {
rv = new String( lineBytes.getBytes(),
StandardCharsets.UTF_8 );
}
return rv;
}
Затем позиция файла может быть получена из RandomAccessFile непосредственно перед вызовом этого метода. С учетом RandomAccessFile, на который ссылается in
:
long startPos = in.getFilePointer();
String line = readNextLineAsUTF8( in );