You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 22:46:06