Python如何不改变编码替换二进制EDF文件中的指定字符
问题描述
需要对二进制编码的EDF文件做字符替换,将文件第一行中所有出现的°字符替换为?。
不指定二进制读取模式打开文件时会抛出如下异常:
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xb0 in position 3008: invalid start byte
触发异常的代码:
with open('heartbeat-baseline-700001.edf') as fin: lines = fin.readlines()
自行编写的替换代码运行后异常:生成的文件大小从原文件的79MB骤降至7KB,完全无法正常使用,代码如下:
with open("heartbeat-baseline-700001.edf", "rb") as text_file: lines = text_file.readlines() lines[1] = lines[1].replace(str.encode('°'), str.encode('?')) for i,line in enumerate(lines): with open('heartbeat-baseline-700001_python.edf', 'wb') as fout: fout.write(line)
问题原因
代码存在三个核心问题:
- 行索引错误:Python列表下标从0开始计数,需要修改的第一行内容对应
lines[0],修改lines[1]实际操作的是文件第二行,完全没有命中目标修改位置。 - 写入逻辑错误:打开输出文件的逻辑被放在了遍历行的循环内部,且打开模式为
wb(覆盖写入),每次循环都会清空文件之前写入的内容,循环结束后文件中仅会保留最后一行的内容,这就是文件大小骤降的根本原因。 - 编码隐患:直接调用
str.encode('°')会使用系统默认编码做转换,不同运行环境下生成的字节序列可能不一致,很容易出现匹配不到目标字符的问题。
正确实现方案
EDF是格式固定的二进制文件,根据规范,文件第一行头信息固定为256字节长度,不需要将整个79MB文件全量读入内存拆分行,仅需要读取前256字节做替换,剩余内容直接流式复制即可,全程二进制操作不会破坏文件原有编码和结构,实现代码如下:
# 同时以二进制模式打开源文件和目标文件 with open("heartbeat-baseline-700001.edf", "rb") as fin, open("heartbeat-baseline-700001_python.edf", "wb") as fout: # 仅读取第一行固定长度的头信息做替换,EDF头默认使用latin1单字节编码 first_header = fin.read(256) replaced_header = first_header.replace('°'.encode('latin1'), '?'.encode('latin1')) fout.write(replaced_header) # 剩余所有二进制内容原封不动写入目标文件 fout.write(fin.read())
说明:选择latin1编码做字节转换是因为EDF规范明确要求头字段使用兼容ASCII的latin1(ISO-8859-1)编码,°在该编码下对应的单字节正好是报错信息中提到的0xb0,可以保证100%匹配目标字符,不会出现编码不匹配的问题。
内容的提问来源于stack exchange,提问作者Warren Manuel
相关产品推荐
相关产品推荐

