Is there Python 3 module to read .dbf files written by ArcGIS for Desktop?

As all Python programmers, I will

  • first see the Python package Index for dbf files (it is specified if they are not compatible with Python 3 or Python 2). dbfread, for example, works with Python 2.x and Python 3.x.
  • a geospatial modules like GDAL/OGR can be used but it needs to be compiled (Linux, Mac OS X). For the Windows users, Christoph Gohlke provides the Unofficial Windows Binaries for Python Extension Packages with GDAL/OGR (Python 2.x and Python 3.x)
  • and a pure Python geospatial module like Pyshp (Python 2.x or 3.x) can also read dbf files
  • and finally, If you know the structure of a dbf file ( Data File Header Structure for the dBASE Version 7 Table File , for example), it is not complicated to program a reader in pure Python with the struct module

I adapted this function for python 2,7 to python 3,x, works for me, but no warranty ... :)

def dbfreader(f): #Found on http://code.activestate.com/recipes/362715-dbf-
reader-and-writer/ - by Raymond Hettinger
"""Returns an iterator over records in a Xbase DBF file.

The first row returned contains the field names.
The second row contains field specs: (type, size, decimal places).
Subsequent rows contain the data records.
If a record is marked as deleted, it is skipped.

File should be opened for binary reads.
Adapted for python 3,5 20-09-2017
"""
# See DBF format spec at:
#     http://www.pgts.com.au/download/public/xbase.htm#DBF_STRUCT

numrec, lenheader = struct.unpack('<xxxxLH22x', f.read(32))
numfields = (lenheader - 33) // 32

fields = []
for fieldno in range(numfields): #PYTHON3
    name, typ, size, deci = struct.unpack('<11sc4xBB14x', f.read(32))
    name=name.decode('utf-8') #PYTHON3
    typ=typ.decode('utf-8') #PYTHON3
    name = name.replace('\0', '')       # eliminate NULs from string
    fields.append((name, typ, size, deci))
yield [field[0] for field in fields]
yield [tuple(field[1:]) for field in fields]

terminator = f.read(1)
terminator=terminator.decode('utf-8') #PYTHON3
assert terminator == '\r'

fields.insert(0, ('DeletionFlag', 'C', 1, 0))
fmt = ''.join(['%ds' % fieldinfo[2] for fieldinfo in fields])
fmtsiz = struct.calcsize(fmt)
for i in range(numrec):
    recordb = struct.unpack(fmt, f.read(fmtsiz))
    record=[]
    for j in range(0,len(recordb)):
        record.append(recordb[j].decode('utf-8')) #PYTHON3
    if record[0] != ' ':
        continue                        # deleted record
    result = []
    for (name, typ, size, deci), value in zip(fields, record):
        if name == 'DeletionFlag':
            continue
        if typ == "N":
            value = value.replace('\0', '').lstrip()
            if (value == '') or "*" in value:
                value = -99
            elif deci:
                value = decimal.Decimal(value)
            else:
                value = int(value)
        elif typ == 'D':
            y, m, d = int(value[:4]), int(value[4:6]), int(value[6:8])
            value = datetime.date(y, m, d)
        elif typ == 'L':
            value = (value in 'YyTt' and 'T') or (value in 'NnFf' and 'F') or '?'
        elif typ == 'F':
            value = float(value)
        result.append(value)
    yield result