def dbfreader(f, names, nullreplace=None):
    """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.

    names is the field names to extract. The value of nullreplace is
    used with data of type 'N' as a replacement for '\0'.

    File should be opened for binary reads.

    """
    # 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 = [FI('DeletionFlag', 'C', 1, 0,
                 '1s', struct.calcsize('1s'), True, 0)] # discarded in main loop

    for fieldno in xrange(numfields):
        name, typ, size, deci = struct.unpack('<11sc4xBB14x', f.read(32))
        name = name.replace('\0', '')       # eliminate NULs from string
        fmt = str(size) + 's'
        prev = fields[fieldno]
        fi = FI(name, typ, size, deci, fmt, struct.calcsize(fmt), name in names,
                prev.seekme + prev.size)
        fields.append(fi)

    selfields = [field for field in fields if field.keep]
    yield [field.name for field in selfields[1:]]
    yield [tuple(field[1:4]) for field in selfields[1:]]

    terminator = f.read(1)
    assert terminator == '\r'

    for i in xrange(numrec):
        refaddr = f.tell()
        record = []
        for field in selfields:
            f.seek(refaddr + field.seekme)
            record.append(struct.unpack(field.fmt, f.read(field.fmtsiz))[0])

        if record[0] != ' ':
            continue                        # deleted record
        result = []
        for sf, value in itertools.izip(selfields, record):
            if sf.name == 'DeletionFlag':
                continue
            if sf.typ == "N":
                value = value.replace('\0', '').lstrip()
                if value == '':
                    value = nullreplace
                elif sf.deci:
                    value = decimal.Decimal(value)
                else:
                    value = int(value)
            elif sf.typ == 'D':
                y, m, d = int(value[:4]), int(value[4:6]), int(value[6:8])
                value = datetime.date(y, m, d)
            elif sf.typ == 'L':
                value = (value in 'YyTt' and 'T') or (value in 'NnFf' and 'F') or '?'
            elif sf.typ == 'F':
                value = float(value)
            result.append(value)
        f.seek(refaddr + fields[-1].seekme + fields[-1].fmtsiz)
        yield result
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.

    names is the field names to extract. The value of nullreplace is
    used with data of type 'N' as a replacement for ''.

    File should be opened for binary reads.

    
recipes/Python/580696_DBF_reader_writer__selective_fields/recipe-580696.py