EDF 文件「第 109 组只写完 3 个通道」时的结构、Resume 如何发现与如何修复

本文回答一个具体场景:磁盘上已有 108 条完整 data record,第 109 条只按通道顺序写入了前 3 个通道就中断——此时文件长什么样、prepareForResume 如何「发现」不完整、以及「修复」究竟做了什么。实现依据为工程内 EDFwriter.javawritePhysicalSamplesprepareForResume)。

1. 前提:一条完整 data record 多长?

以下与常见 EDF+、5 路数据 + 1 路 EDF Annotations、每 record 持续 1 秒、各通道每 record 样本数为 500、500、50、50、50 的配置一致(与 configureEdfWriter 类场景一致)。

1.1 头区长度

headerSize = (edfsignals + nr_annot_chns + 1) × 256 = (5 + 1 + 1) × 256 = 1792 字节

1.2 每条完整 data record 的字节数 recordsize

实现逻辑(概念上):

  1. 各通道本 record 内样本数求和:500 + 500 + 50 + 50 + 50 = 1150
  2. EDF 16 位:再 × 22300 字节(纯数据通道)。
  3. 每条 record 末尾还有 注释轨 在本 record 内的固定宽度:114 × nr_annot_chnsnr_annot_chns = 1 时为 114 字节)。

因此:

recordsize = 1150 × 2 + 114 = 2414 字节/条(完整 record)

1.3 单通道一次 writePhysicalSamples 往文件追加多少字节?

当前通道 edfsignal 追加:param_smp_per_record[edfsignal] × 2 字节。

  • 通道 0:500 × 2 = 1000
  • 通道 1:500 × 2 = 1000
  • 通道 2:50 × 2 = 100
  • 通道 3、4 若写完:各 50 × 2 = 100

只有写完所有数据通道后,库才会写入本 record 的 TAL/注释块write_tal),并把 datarecords 加 1。若只写完 3 个通道就停:不会写第 109 条的注释块,datarecords 仍为 108


2. 此时文件内容结构(从字节视角)

设已成功写入 108 条完整 record,第 109 条只写了通道 0、1、2

文件总长度:

L = headerSize + 108 × recordsize + (1000 + 1000 + 100)
  = 1792 + 108 × 2414 + 2100

结构可画成:

[ 1792 字节:头区 ]
[ 108 × 2414 字节:第 1~108 条完整 data record,每条含 5 路数据 + 本 record 注释轨 ]
[ 2100 字节:第 109 条 record 的「半截」——仅 ch0/ch1/ch2 的二进制,无 ch4/ch5,也无本 record 的 114 字节注释轨 ]

注意:这里的「第 109 组」是业务口语;在 EDF 逻辑条数里,第 109 条 record 并未形成,文件里只多了一段 无法单独构成一条 record 的尾部二进制


3. Resume 时如何「定位到第 109 组不全」?

prepareForResume 不做「第几组、缺哪几路」的语义诊断,只做 长度与 recordsize 的整除对齐

payloadSize = L - headerSize
completeRecords = floor(payloadSize / recordsize)
repairedLength = headerSize + completeRecords × recordsize
truncatedBytes = L - repairedLength

代入数字:

payloadSize = 108 × 2414 + 2100
completeRecords = floor((108 × 2414 + 2100) / 2414) = 108 + floor(2100 / 2414) = 108

因为 2100 < 2414,多出来的 2100 字节不够凑成第 109 条完整 record,故 completeRecords 仍为 108

结论:实现上不是「识别出第 109 组缺两路」,而是 「payload 长度对单条 record 长度取 floor,余数即未完成的尾部」;在你给的场景里,余数恰好等于「只写了 3 路」的那 2100 字节。


4. 「修复」具体做了什么?

prepareForResume 末尾(概念步骤):

  1. file_out.length() != repairedLength,则 setLength(repairedLength) —— 物理截断文件,删除尾部 truncatedBytes(此处为 2100) 字节。
  2. datarecords = completeRecords(此处为 108),并把写指针 seekrepairedLength,下一轮 writePhysicalSamples下一条 record 的起点通道 0 重新开始。

因此所谓修复是:丢弃无法构成完整 data record 的尾部字节,而不是在原地补写通道 3、4 与注释轨。未完成的「伪第 109 条」已写部分会被整体删掉;若继续采集,相当于重新从「第 109 条 record」的开头写起。


5. 小结表

问题 答案
文件里算不算已有 109 条 record? 不算;逻辑上仍是 108 条,尾部多 2100 B 零头。
如何「发现」不全? (L - headerSize) % recordsize ≠ 0floor 后完整条数少于「若写满时的条数」。
修复是否补全通道? setLength 去掉零头,再从头开始写后续 record。

与《EDF+ 文件头区长度与字节布局》笔记配合阅读:头长 headerSizerecordsize 一致时,payload / recordsize 的划分才可靠。