不能用 break 直接检测系统 EOF,而应通过字节流滑动窗口匹配自定义的 "%%EOF" 结束标记;需用 InputStream 逐字节读取、避免编码干扰,并在缓冲区中搜索完整序列(含换行符),匹配成功后截取并退出。

不能靠 break 直接“检测 EOF 标记”来停止解析——因为标准的文件末尾(end-of-file)是操作系统级状态,不是某个可搜索的字符串;而你提到的 “%%EOF” 是自定义分隔符,属于业务数据的一部分,和系统 EOF 完全不同。混淆这两者,是二进制文件解析出错的常见根源。
明确区分:系统 EOF vs 自定义结束标记
系统 EOF 表示文件字节流真正结束,由底层 I/O 返回 0 字节或 -1 指示;而 "%%EOF" 是 PDF 合并文件中人为插入的 ASCII 字符序列(共 6 字节:25 25 45 4F 46 0A),需在字节流中主动扫描识别。
因此,“遇到 %%EOF 就 break” 是完全可行的,但必须:
- 用
InputStream(非 Reader)逐字节或批量读取原始字节,避免编码解码破坏二进制内容 - 在缓冲区中实现滑动窗口匹配,不能依赖
readLine()(它按换行切分,会截断跨块的"%%EOF") - 把
break放在匹配成功后,且确保已完整写出当前段落(如一个 PDF 子项)
用 byte[] 缓冲 + 滑动匹配实现 %%EOF 检测
核心思路:维持一个固定大小缓冲区(如 8192 字节),每次读入新数据后,在缓冲区末尾附近检查是否出现 "%%EOF\n" 或 "%%EOF\r\n"。推荐使用 Arrays.equals() 或手动比对:
- 读取时用
inputStream.read(buffer, offset, length)获取真实字节数 - 维护一个
totalRead计数器,记录当前缓冲区有效字节数 - 从
Math.max(0, totalRead - 10)开始向前扫描,避免漏掉跨块边界的情况 - 一旦发现匹配,立即
break循环,并用totalRead - matchIndex - 6算出该段实际长度
典型错误:用 BufferedReader 读二进制
像下面这种写法必然失败:
BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(f), "UTF-8"));
原因有三:
- UTF-8 解码会把多字节序列误判为非法字符,触发替换或异常
-
readLine()自动丢弃\r\n,导致"%%EOF\n"变成"%%EOF",但你也失去了换行位置信息 - 无法获取原始字节偏移,后续写回二进制文件时长度错乱、内容损坏
简洁可用的 Java 片段示意
以下逻辑适用于单次提取首个以 "%%EOF" 结尾的块(如第一个 PDF):
- 分配
byte[] buf = new byte[8192] - 循环调用
int n = in.read(buf, pos, buf.length - pos) - 若
n == -1→ 真正到达系统 EOF,break 并报错“未找到 %%EOF” - 否则更新
pos += n,并在buf[0..pos)中搜索new byte[]{'%', '%', 'E', 'O', 'F', '\n'} - 搜到后,用
Arrays.copyOf(buf, foundIndex + 6)截取完整段,然后break










