Ниже приведены две функции, одна из которых возвращает последнюю непустую строку файла без загрузки или пошагового выполнения по всему файлу, а вторая возвращает последние N строк файла, не проходя по всему файлу. :
То, что делает хвост, это масштабирование прямо до последнего символа файла, затем шаг за шагом, символ за символом, запись того, что он видит, пока не обнаружит разрыв строки. Как только он находит разрыв строки, он выходит из цикла. Переворачивает то, что было записано, бросает его в строку и возвращает. 0xA - это новая строка, а 0xD - возврат каретки.
Если ваши окончания строки \r\n
или crlf
или какой-либо другой "двойной перевод новой строки в стиле новой строки", то вам нужно будет указать n * 2 строки, чтобы получить последние n строк, потому что она рассчитывает 2 строки для каждой строки.
public String tail( File file ) {
RandomAccessFile fileHandler = null;
try {
fileHandler = new RandomAccessFile( file, "r" );
long fileLength = fileHandler.length() - 1;
StringBuilder sb = new StringBuilder();
for(long filePointer = fileLength; filePointer != -1; filePointer--){
fileHandler.seek( filePointer );
int readByte = fileHandler.readByte();
if( readByte == 0xA ) {
if( filePointer == fileLength ) {
continue;
}
break;
} else if( readByte == 0xD ) {
if( filePointer == fileLength - 1 ) {
continue;
}
break;
}
sb.append( ( char ) readByte );
}
String lastLine = sb.reverse().toString();
return lastLine;
} catch( java.io.FileNotFoundException e ) {
e.printStackTrace();
return null;
} catch( java.io.IOException e ) {
e.printStackTrace();
return null;
} finally {
if (fileHandler != null )
try {
fileHandler.close();
} catch (IOException e) {
/* ignore */
}
}
}
Но вам, вероятно, не нужна последняя строка, вам нужны последние N строк, поэтому используйте вместо этого:
public String tail2( File file, int lines) {
java.io.RandomAccessFile fileHandler = null;
try {
fileHandler =
new java.io.RandomAccessFile( file, "r" );
long fileLength = fileHandler.length() - 1;
StringBuilder sb = new StringBuilder();
int line = 0;
for(long filePointer = fileLength; filePointer != -1; filePointer--){
fileHandler.seek( filePointer );
int readByte = fileHandler.readByte();
if( readByte == 0xA ) {
if (filePointer < fileLength) {
line = line + 1;
}
} else if( readByte == 0xD ) {
if (filePointer < fileLength-1) {
line = line + 1;
}
}
if (line >= lines) {
break;
}
sb.append( ( char ) readByte );
}
String lastLine = sb.reverse().toString();
return lastLine;
} catch( java.io.FileNotFoundException e ) {
e.printStackTrace();
return null;
} catch( java.io.IOException e ) {
e.printStackTrace();
return null;
}
finally {
if (fileHandler != null )
try {
fileHandler.close();
} catch (IOException e) {
}
}
}
Вызовите вышеуказанные методы, как это:
File file = new File("D:\\stuff\\huge.log");
System.out.println(tail(file));
System.out.println(tail2(file, 10));
Внимание * * 1023
На диком западе юникода этот код может привести к неправильному выводу этой функции. Например "Мэри?" Вместо "Мэри". Символы с шляпами, акцентами, китайскими иероглифами и т. Д. Могут привести к неправильному выводу, поскольку акценты добавляются в качестве модификаторов после символа. Реверсирование составных символов изменяет характер личности персонажа при обращении. Вам придется выполнить полный набор тестов на всех языках, с которыми вы планируете его использовать.
Для получения дополнительной информации об этой проблеме обращения Unicode прочитайте это:
http://msmvps.com/blogs/jon_skeet/archive/2009/11/02/omg-ponies-aka-humanity-epic-fail.aspx