Java 文件中最后一个换行符的位置
Position of the last newline from a section of a file with Java
如何有效地确定文件特定部分的最后一个换行符的位置?
例如我试过这个
BufferedReader br = new BufferedReader(new FileReader(file));
long length = file.length();
String line = null;
int tailLength = 0;
while ((line = br.readLine()) != null) {
System.out.println(line);
tailLength = line.getBytes().length;
}
int returnValue = length - tailLength;
但这只会 return 整个文件中最后一个换行符的位置,而不是文件一部分中最后一个换行符的位置。此部分将由 int start;
和 int end;
指示
不幸的是你不能,我不得不使用 RandomAccessFile
,它有 getFilePointer()
方法,你可以在 readLine()
之后调用它,但它非常慢而且不是 UTF-8-知道了。
我最终实现了自己的字节计数行 reader。
当面对包含 unicode、格式错误或二进制内容的文件时,您的幼稚解决方案将会失败。
我认为最有效的方法是从文件末尾开始分块读取。然后,向后搜索第一行。
即
import java.io.IOException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
public class FileUtils {
static final int CHUNK_SIZE = 8 * 1024;
public static long getLastLinePosition(Path path) throws IOException {
try (FileChannel inChannel = FileChannel.open(path, StandardOpenOption.READ);
@SuppressWarnings("unused")
FileLock lock = inChannel.tryLock(0, Long.MAX_VALUE, true)) {
long fileSize = inChannel.size();
long mark = fileSize;
long position;
boolean ignoreCR = false;
while (mark > 0) {
position = Math.max(0, mark - CHUNK_SIZE);
MappedByteBuffer mbb = inChannel.map(FileChannel.MapMode.READ_ONLY, position, Math.min(mark, CHUNK_SIZE));
byte[] bytes = new byte[mbb.remaining()];
mbb.get(bytes);
for (int i = bytes.length - 1; i >= 0; i--, mark--) {
switch (bytes[i]) {
case '\n':
if (mark < fileSize) {
return mark;
}
ignoreCR = true;
break;
case '\r':
if (ignoreCR) {
ignoreCR = false;
} else if (mark < fileSize) {
return mark;
}
break;
}
}
mark = position;
}
}
return 0;
}
}
测试文件:
abc\r\n
1234\r\n
def\r\n
输出:11
了解有关 java.nio.channels.FileChannel
and java.nio.MappedByteBuffer
的更多信息:
- http://tutorials.jenkov.com/java-nio/file-channel.html
- https://examples.javacodegeeks.com/core-java/nio/filechannel/java-nio-channels-filechannel-example/
- https://examples.javacodegeeks.com/core-java/nio/mappedbytebuffer/java-mappedbytebuffer-example/
- http://tutorials.techmytalk.com/2014/11/05/java-nio-memory-mapped-files/
- http://javarevisited.blogspot.nl/2012/01/memorymapped-file-and-io-in-java.html
编辑 :
如果您使用的是 Java6,请将这些更改应用于以上代码:
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
public class FileUtils {
static final int CHUNK_SIZE = 8 * 1024;
public static long getLastLinePosition(String name) throws IOException {
FileChannel inChannel = null;
FileLock lock = null;
try {
inChannel = new RandomAccessFile(name, "r").getChannel();
lock = inChannel.tryLock(0, Long.MAX_VALUE, true);
// ...
} finally {
if (lock != null) {
lock.release();
}
if (inChannel != null) {
inChannel.close();
}
}
return 0;
}
}
关于选择理想缓冲区大小的提示:
如何有效地确定文件特定部分的最后一个换行符的位置?
例如我试过这个
BufferedReader br = new BufferedReader(new FileReader(file));
long length = file.length();
String line = null;
int tailLength = 0;
while ((line = br.readLine()) != null) {
System.out.println(line);
tailLength = line.getBytes().length;
}
int returnValue = length - tailLength;
但这只会 return 整个文件中最后一个换行符的位置,而不是文件一部分中最后一个换行符的位置。此部分将由 int start;
和 int end;
不幸的是你不能,我不得不使用 RandomAccessFile
,它有 getFilePointer()
方法,你可以在 readLine()
之后调用它,但它非常慢而且不是 UTF-8-知道了。
我最终实现了自己的字节计数行 reader。
当面对包含 unicode、格式错误或二进制内容的文件时,您的幼稚解决方案将会失败。
我认为最有效的方法是从文件末尾开始分块读取。然后,向后搜索第一行。
即
import java.io.IOException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
public class FileUtils {
static final int CHUNK_SIZE = 8 * 1024;
public static long getLastLinePosition(Path path) throws IOException {
try (FileChannel inChannel = FileChannel.open(path, StandardOpenOption.READ);
@SuppressWarnings("unused")
FileLock lock = inChannel.tryLock(0, Long.MAX_VALUE, true)) {
long fileSize = inChannel.size();
long mark = fileSize;
long position;
boolean ignoreCR = false;
while (mark > 0) {
position = Math.max(0, mark - CHUNK_SIZE);
MappedByteBuffer mbb = inChannel.map(FileChannel.MapMode.READ_ONLY, position, Math.min(mark, CHUNK_SIZE));
byte[] bytes = new byte[mbb.remaining()];
mbb.get(bytes);
for (int i = bytes.length - 1; i >= 0; i--, mark--) {
switch (bytes[i]) {
case '\n':
if (mark < fileSize) {
return mark;
}
ignoreCR = true;
break;
case '\r':
if (ignoreCR) {
ignoreCR = false;
} else if (mark < fileSize) {
return mark;
}
break;
}
}
mark = position;
}
}
return 0;
}
}
测试文件:
abc\r\n
1234\r\n
def\r\n
输出:11
了解有关 java.nio.channels.FileChannel
and java.nio.MappedByteBuffer
的更多信息:
- http://tutorials.jenkov.com/java-nio/file-channel.html
- https://examples.javacodegeeks.com/core-java/nio/filechannel/java-nio-channels-filechannel-example/
- https://examples.javacodegeeks.com/core-java/nio/mappedbytebuffer/java-mappedbytebuffer-example/
- http://tutorials.techmytalk.com/2014/11/05/java-nio-memory-mapped-files/
- http://javarevisited.blogspot.nl/2012/01/memorymapped-file-and-io-in-java.html
编辑 :
如果您使用的是 Java6,请将这些更改应用于以上代码:
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
public class FileUtils {
static final int CHUNK_SIZE = 8 * 1024;
public static long getLastLinePosition(String name) throws IOException {
FileChannel inChannel = null;
FileLock lock = null;
try {
inChannel = new RandomAccessFile(name, "r").getChannel();
lock = inChannel.tryLock(0, Long.MAX_VALUE, true);
// ...
} finally {
if (lock != null) {
lock.release();
}
if (inChannel != null) {
inChannel.close();
}
}
return 0;
}
}
关于选择理想缓冲区大小的提示: