Java中如何按行读取文本文件并指定字节偏移量与限制(不加载全文件)
Java按行读取指定字节范围的文本文件
你需要读取文件的0-9500字节(排除末尾500字节的签名),同时按行处理且不加载整个文件到内存,以下是两种可靠的实现方式:
方法一:使用RandomAccessFile(传统IO)
RandomAccessFile支持直接定位到指定偏移量,同时可按行读取,只需控制累计读取字节数不超过9500即可避免读到签名部分。
import java.io.RandomAccessFile; import java.io.IOException; public class LineReaderWithOffset { public static void main(String[] args) { final long START_OFFSET = 0; final long MAX_READ_BYTES = 9500; String filePath = "your-target-file.txt"; try (RandomAccessFile raf = new RandomAccessFile(filePath, "r")) { raf.seek(START_OFFSET); // 定位到起始偏移位置 long totalBytesRead = 0; String line; long lastPos = START_OFFSET; while (totalBytesRead < MAX_READ_BYTES && (line = raf.readLine()) != null) { long currentPos = raf.getFilePointer(); // 计算当前行的实际字节数(包含换行符) long lineByteCount = currentPos - lastPos; totalBytesRead += lineByteCount; // 处理当前行(若文件是UTF-8编码,需转码避免乱码) String decodedLine = new String(line.getBytes("ISO-8859-1"), "UTF-8"); System.out.println(decodedLine); // 检查是否即将超出读取限制,提前终止 if (totalBytesRead >= MAX_READ_BYTES) { break; } lastPos = currentPos; } } catch (IOException e) { e.printStackTrace(); } } }
方法一注意事项
raf.readLine()默认使用系统编码,若文件为UTF-8等非默认编码,必须手动转码,否则会出现乱码。- 通过文件指针位置差计算行字节数,能准确统计累计读取量,避免读到末尾的签名内容。
方法二:使用FileChannel + CharsetDecoder(NIO高效实现)
NIO的FileChannel可精准控制读取的字节范围,结合CharsetDecoder能正确处理多字节编码(如UTF-8),同时用小缓冲区降低内存占用。
import java.io.FileInputStream; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import java.nio.charset.CoderResult; import java.nio.CharBuffer; public class NIOByteRangeLineReader { public static void main(String[] args) { final long START_OFFSET = 0; final long MAX_READ_BYTES = 9500; String filePath = "your-target-file.txt"; Charset charset = Charset.forName("UTF-8"); // 根据文件实际编码调整 CharsetDecoder decoder = charset.newDecoder(); try (FileInputStream fis = new FileInputStream(filePath); FileChannel channel = fis.getChannel()) { ByteBuffer byteBuffer = ByteBuffer.allocate(1024); // 1KB小缓冲区,内存占用极低 CharBuffer charBuffer = CharBuffer.allocate(1024); long totalBytesRead = 0; long currentPosition = START_OFFSET; StringBuilder lineBuilder = new StringBuilder(); while (totalBytesRead < MAX_READ_BYTES) { // 计算本次可读取的最大字节数,避免超出限制 long bytesToRead = Math.min(MAX_READ_BYTES - totalBytesRead, byteBuffer.capacity()); int readBytes = channel.read(byteBuffer, currentPosition); if (readBytes == -1) break; totalBytesRead += readBytes; currentPosition += readBytes; byteBuffer.flip(); // 解码字节为字符,处理可能的跨缓冲区字符 CoderResult result = decoder.decode(byteBuffer, charBuffer, totalBytesRead >= MAX_READ_BYTES); charBuffer.flip(); // 解析每行内容 while (charBuffer.hasRemaining()) { char c = charBuffer.get(); if (c == '\n') { // 处理Windows下的\r换行符 String line = lineBuilder.toString().replace("\r", ""); System.out.println(line); lineBuilder.setLength(0); } else { lineBuilder.append(c); } } charBuffer.compact(); byteBuffer.compact(); } // 处理文件末尾无换行符的最后一行 if (lineBuilder.length() > 0) { System.out.println(lineBuilder.toString().replace("\r", "")); } } catch (Exception e) { e.printStackTrace(); } } }
方法二优势
- 精准控制读取范围,绝对不会读取到末尾的500字节签名。
- 小缓冲区设计,内存占用远低于加载整个文件的方式。
- 原生支持多字节编码解码,彻底避免乱码问题。
内容的提问来源于stack exchange,提问作者KUL
相关产品推荐
相关产品推荐

