使用 dbf 包编辑 .dbf 文件时出现 BadDataError

BadDataError when editing a .dbf file using dbf package

我最近在 unix 系统上从大气模型 (HYSPLIT) 生成了数千个 shapefile 输出和随附的 .dbf 文件。转换器 txt2dbf 用于将 shapefile 属性表(文本文件)转换为 .dbf。

不幸的是,出了点问题(可能是 separator/field 长度错误),因为输出的 .dbf 文件有 2 个问题,如下:

  1. dbf 的某些字段包含不应存在的数据。此数据已从相邻字段“溢出”。
  2. 添加了一个不应该存在的附加字段(它实际上来自文本文件第一条记录的一部分,“1000 201”)。

这是输出 dbf 中第一条记录的示例(使用 dbview unix 包检索):

Trajnum : 1001 2
Yyyymmdd : 0111231 2
Time : 300
Level : 0.
1000 201:

这是我的预期:

Trajnum : 1000
Yyyymmdd : 20111231
Time : 2300
Level : 0.

另外,我正在研究如何防止这种情况再次发生,但理想情况下我希望能够修复现有的 .dbf 文件。不幸的是,每个模型的文本文件都被删除了 运行,因此“修复”.dbf 文件是唯一的选择。

我解决上述问题的方法是:

  1. 使用dbf.add_fieldsdbf.write(python包dbf)从确实存在的字段中提取信息到新变量,然后删除旧的不正确的字段使用 dbf.delete_fields.
  2. 删除不需要的附加字段。

这是我试过的:

        with dbf.Table(db) as db:
            db.add_fields("TRAJNUMc C(4)") #create new fields
            db.add_fields("YYYYMMDDc C(8)")
            db.add_fields("TIMEc C(4)")
            for record in db: #extract data from fields
                    dbf.write(TRAJNUMc=int(str(record.Trajnum)[:4]))
                    dbf.write(YYYYMMDDc=int(str(record.Trajnum)[-1:] + str(record.Yyyymmdd)[:7]))
                    dbf.write(TIMEc=record.Yyyymmdd[-1:] + record.Time[:])
            db.delete_fields('Trajnum') # delete the incorrect fields
            db.delete_fields('Yyyymmdd')
            db.delete_fields('Time')
            db.delete_fields('1000 201') #delete the unwanted field
            db.pack()

但这会产生以下错误:

dbf.ver_2.BadDataError: record data is not the correct length (should be 31, not 30)

鉴于 txt2dbf 转换存在的明显问题,我发现记录数据长度存在错误并不奇怪。但是,这是否意味着该文件已完全损坏并且我无法提取我需要的信息(令人沮丧,因为我可以看到它存在)?


编辑:

与其尝试编辑 'bad'.dbf 文件,这似乎是一种更好的方法:1. 从坏文件中将所需数据提取到文本中,然后 2. 写入新的 dbf。 (参见下面 Ethan Furman 的 comments/answer)。


编辑:

我需要 fix/recover 数据的错误 .dbf 文件示例可在此处找到:

https://www.dropbox.com/s/9y92f7m88a8g5y4/p0001120110.dbf?dl=0

可在此处找到创建错误 dbf 文件的示例 .txt 文件:

https://www.dropbox.com/s/d0f2c0zehsyy8ab/attTEST.txt?dl=0

要修复数据并重新创建原始文本文件,此代码段应该有所帮助:

import dbf

table = dbf.Table('/path/to/scramble/table.dbf')
with table:
    fixed_data = []
    for record in table:
        # convert to str/bytes while skipping delete flag
        data = record._data[1:].tostring()
        trajnum = data[:4]
        ymd = data[4:12]
        time = data [12:16]
        level = data[16:].strip()
        fixed_data.extend([trajnum, ymd, time, level])

new_file = open('repaired_data.txt', 'w')
for line in fixed_data:
    new_file.write(','.join(line) + '\n')

假设您所有的数据文件看起来都像您的示例(大 IF 是数据没有嵌入逗号),那么这段粗略的代码应该有助于将您的文本文件转换为 dbfs:

raw_data = open('some_text_file.txt').read().split('\n')
final_table = dbf.Table(
        'dest_table.dbf',
        'trajnum C(4); yyyymmdd C(8); time C(4); level C(9)',
        )
with final_table:
    for line in raw_data:
        fields = line.split(',')
        final_table.append(tuple(fields))

# table has been populated and closed

当然,如果您愿意,您可以更高级并使用实际日期和数字字段:

# dbf string becomes
'trajnum N; yyyymmdd D; time C(4), level N'

#appending data loop becomes
    for line in raw_data:
        trajnum, ymd, time, level = line.split(',')
        trajnum = int(trajnum)
        ymd = dbf.Date(ymd[:4], ymd[4:6], ymd[6:])
        level = int(level)
        final_table.append((trajnum, ymd, time, level))