Java文件分段读取异常:重复读取起始内容,如何指定读取位置?
问题根源
你每次调用readData()方法时,都会重新创建一个FileReader实例。而FileReader每次初始化都会从文件的起始位置开始读取,这就是为什么你永远只能读到前5个字符的原因——每次调用方法都相当于从头读一遍文件。
解决方案
方案一:将读取器作为类成员变量,保持读取状态
把文件读取器(推荐用BufferedReader,比FileReader效率更高)声明为类的成员变量,而不是在readData()方法内每次创建。这样读取器会持续保持当前的读取位置,每次调用方法时都会从上次结束的地方继续读取。
修改后的代码示例:
// 类成员变量,初始化一次即可 private BufferedReader reader; // 初始化读取器的方法(可在类的构造函数里调用) public void initReader() throws IOException { try { this.reader = new BufferedReader(new FileReader(inputFileName)); } catch (IOException e){ System.out.println("文件读取器初始化失败"); System.out.println(e.getMessage()); System.exit(0); } } public int readData() throws IOException { if (reader == null) { throw new IllegalStateException("读取器未初始化,请先调用initReader()"); } System.out.println("创建分段..."); System.out.println("----------------------------------------------------"); int readLength; char[] segCharsBuf; if (this.remainingBytes < this.maxPayload) { segCharsBuf = new char[(int) this.remainingBytes]; readLength = reader.read(segCharsBuf, 0, (int) this.remainingBytes); // 处理文件提前结束的情况 if (readLength == -1) { return -1; } this.dataSeg.setSize(readLength); // 注意:不要用Arrays.toString,会生成带括号和逗号的格式 this.dataSeg.setPayLoad(new String(segCharsBuf, 0, readLength)); this.remainingBytes -= readLength; this.dataSeg.setSq(((int)this.fileSize - (int)this.remainingBytes) / this.maxPayload); System.out.println("分段编号 " + this.dataSeg.getSq() +" 创建完成 (" + this.dataSeg.getSize() + " 字符)"); System.out.println("----------------------------------------------------"); return -1; } else { segCharsBuf = new char[this.maxPayload]; readLength = reader.read(segCharsBuf, 0, this.maxPayload); if (readLength == -1) { return -1; } this.dataSeg.setSize(readLength); this.dataSeg.setPayLoad(new String(segCharsBuf, 0, readLength)); this.remainingBytes -= readLength; this.dataSeg.setSq(((int)this.fileSize - (int)this.remainingBytes) / this.maxPayload); System.out.println("分段编号 " + this.dataSeg.getSq() +" 创建完成 (" + this.dataSeg.getSize() + " 字符)"); System.out.println("----------------------------------------------------"); return 0; } }
关键注意点:
- 必须在第一次调用
readData()前执行initReader()初始化读取器 - 替换
Arrays.toString(segCharsBuf)为new String(segCharsBuf, 0, readLength),避免生成错误的payload格式 - 每次读取后要正确更新
remainingBytes,减去实际读取的字符数
方案二:使用RandomAccessFile实现随机定位
如果需要更灵活的读取位置控制(比如中途跳转到指定位置),可以使用RandomAccessFile。它支持seek()方法直接定位到文件的指定字节位置,适合精确控制读取起点的场景。
示例代码:
private RandomAccessFile raf; private long currentPosition; // 记录当前读取的字节位置 public void initRandomAccessFile() throws IOException { try { this.raf = new RandomAccessFile(inputFileName, "r"); this.currentPosition = 0; } catch (IOException e){ System.out.println("文件读取器初始化失败"); System.out.println(e.getMessage()); System.exit(0); } } public int readData() throws IOException { if (raf == null) { throw new IllegalStateException("读取器未初始化,请先调用initRandomAccessFile()"); } raf.seek(currentPosition); // 定位到上次读取的位置 System.out.println("创建分段..."); System.out.println("----------------------------------------------------"); byte[] byteBuf; int readBytes; String payload; if (this.remainingBytes < this.maxPayload) { byteBuf = new byte[(int) this.remainingBytes]; readBytes = raf.read(byteBuf); if (readBytes == -1) { return -1; } payload = new String(byteBuf, 0, readBytes, StandardCharsets.UTF_8); this.dataSeg.setSize(readBytes); this.remainingBytes -= readBytes; currentPosition += readBytes; } else { byteBuf = new byte[this.maxPayload]; readBytes = raf.read(byteBuf); if (readBytes == -1) { return -1; } payload = new String(byteBuf, 0, readBytes, StandardCharsets.UTF_8); this.dataSeg.setSize(readBytes); this.remainingBytes -= readBytes; currentPosition += readBytes; } this.dataSeg.setPayLoad(payload); this.dataSeg.setSq(((int)this.fileSize - (int)this.remainingBytes) / this.maxPayload); System.out.println("分段编号 " + this.dataSeg.getSq() +" 创建完成 (" + this.dataSeg.getSize() + " 字节)"); System.out.println("----------------------------------------------------"); return remainingBytes > 0 ? 0 : -1; }
关键注意点:
- 此方案按字节计算分段,更符合网络传输的常见场景
- 指定编码为
StandardCharsets.UTF_8,避免文本转换乱码 - 每次读取后更新
currentPosition,确保下次从正确位置开始读取
内容的提问来源于stack exchange,提问作者JB1511
相关产品推荐
相关产品推荐

