使用DBF包写入自定义单字节编码DBF文件的方法咨询
需求说明
需要将包含波斯语字符的内容写入DBF文件,要求使用单字符占1字节的自定义代码页,可选实现思路如下:
- 向dbf表实例传入自定义代码页参数
- 不使用dbf包默认的UTF-8编码,直接向DBF文件写入二进制数据
现有待调整的代码如下:
import dbf man = 'مرد' woman = 'زن' row1 = (man, woman) row2 = (man, woman) with open('./file.dbf', 'w') as f: table = dbf.Table(filename='./file.dbf', field_specs='field1 C(3); field2 C(3)', codepage='customCodePage', on_disk=True) table.open(dbf.READ_WRITE) table.append(row1) table.append(row2) table.close()
实现方法
方法1:通过dbf包传入代码页参数
dbf包的codepage参数支持传入Python环境中已注册的所有编码名称,波斯语场景下通用的单字节编码为cp1256(Windows-1256),该编码为单字节定长编码,完全覆盖常用波斯语字符,每个字符占1字节,直接传入即可满足需求。
如果需要使用完全自定义映射的代码页,可以先通过Python内置codecs模块注册自定义的单字节编码映射,注册完成后将自定义编码名传入codepage参数即可,包会自动完成字符到单字节的转换。
修正后的可运行代码如下:
import dbf man = 'مرد' woman = 'زن' row1 = (man, woman) row2 = (man, woman) # 注意无需提前手动open文件,dbf包会自行处理文件IO,提前打开会触发文件占用问题 table = dbf.Table( filename='./file.dbf', field_specs='field1 C(3); field2 C(3)', codepage='cp1256', # 传入波斯语单字节编码,可替换为自行注册的自定义编码名 on_disk=True ) table.open(dbf.READ_WRITE) table.append(row1) table.append(row2) table.close()
方法2:直接写入二进制数据
如果不想依赖dbf包的默认编码逻辑,可以按照DBF文件格式规范手动拼接二进制内容写入:DBF文件由定长文件头、字段描述块、记录内容块三部分组成,字符字段直接按目标单字节编码将字符串转为字节流,按字段长度截断、补空格后写入对应位置即可。
极简实现代码如下:
import struct # 替换为你的自定义单字节编码 target_encoding = 'cp1256' single_field_len = 3 total_records = 2 # 计算头长度:32字节基础头 + 每个字段32字节描述 + 1字节头终止符 header_total_len = 32 + 32 * 2 + 1 # 计算单条记录长度:1字节删除标记 + 两个字段的长度 single_record_len = 1 + single_field_len * 2 # 拼接基础文件头 file_header = bytearray() file_header.append(0x03) # 无备注的dBase III格式版本号 file_header.extend(struct.pack('<I', total_records)) # 总记录数 file_header.extend(struct.pack('<H', header_total_len)) # 文件头总长度 file_header.extend(struct.pack('<H', single_record_len)) # 单条记录长度 file_header.extend(b'\x00' * 20) # 预留位填充 # 拼接第一个字段描述 field1_desc = bytearray(b'field1\x00') field1_desc.extend(b'\x00' * (11 - len(field1_desc))) # 字段名补0到11字节 field1_desc.append(ord('C')) # 字段类型为字符型 field1_desc.extend(b'\x00' * 4) # 字段地址预留位 field1_desc.append(single_field_len) # 字段长度 field1_desc.append(0x00) # 小数位数为0 field1_desc.extend(b'\x00' * 14) # 预留位填充 file_header.extend(field1_desc) # 拼接第二个字段描述 field2_desc = bytearray(b'field2\x00') field2_desc.extend(b'\x00' * (11 - len(field2_desc))) field2_desc.append(ord('C')) field2_desc.extend(b'\x00' * 4) field2_desc.append(single_field_len) field2_desc.append(0x00) field2_desc.extend(b'\x00' * 14) file_header.extend(field2_desc) file_header.append(0x0D) # 文件头终止标记 # 拼接记录内容 record_content = bytearray() man_b = 'مرد'.encode(target_encoding).ljust(single_field_len, b' ')[:single_field_len] woman_b = 'زن'.encode(target_encoding).ljust(single_field_len, b' ')[:single_field_len] for _ in range(total_records): record_content.append(0x20) # 空格表示该记录未被删除 record_content.extend(man_b) record_content.extend(woman_b) # 二进制写入文件 with open('./file.dbf', 'wb') as f: f.write(file_header) f.write(record_content)
注意事项
- 写入前需要确认所用的单字节编码覆盖了所有待写入的波斯语字符,避免出现字符编码失败、被替换为问号的问题
- 手动拼接二进制时要严格遵守DBF的长度规范,否则生成的文件无法被常规DBF读取工具识别
内容的提问来源于stack exchange,提问作者Hossein Yousefian
相关产品推荐
相关产品推荐

