[DB-SIG] Clipper database?

Frederic Vander Elst fe@phgroup.com
Tue, 07 Aug 2001 16:35:10 +0100


This is a multi-part message in MIME format.
--------------70CC157745C8512996C24A0A
Content-Type: multipart/alternative;
 boundary="------------FD800CBABF6FC5931B58A796"


--------------FD800CBABF6FC5931B58A796
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

Hi,

enclosed the self-documenting code.  Readonly I'm afraid, you'll have to use some ODBC (mxODBC) for writing.

Original comments at top:


> A module for reading dbf files. Tested ONLY on linux.
>
> Author                : Michal Spalinski (mspal@fuw.edu.pl)
> Copying policy: unlimited (do what you want with it)
> Warranty              : none whatsoever
>

I've hacked it a bit and added some error-checking.  If you improve it greatly, please let me/ the list have the update !

-freddie
to reply, try adding a v as the second letter of my uname.


Bill Moore wrote:

> Hi.  I'm new to Python and this list.  Has anyone written an interface to dBASE or Clipper tables?
>
> I would like to be able to view field names and browse the contents of the data file.  Optionally, it would be nice to be able to actually lock a record and modify its contents.
>
> Thanks.
>
> Bill Moore
>
> _______________________________________________
> DB-SIG maillist  -  DB-SIG@python.org
> http://mail.python.org/mailman/listinfo/db-sig

--------------FD800CBABF6FC5931B58A796
Content-Type: text/html; charset=us-ascii
Content-Transfer-Encoding: 7bit

<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html>
<body text="#000000" bgcolor="#FFFFFF" link="#0000CC" vlink="#660066" alink="#660066">
Hi,
<p>enclosed the self-documenting code.&nbsp; Readonly I'm afraid, you'll
have to use some ODBC (mxODBC) for writing.
<p>Original comments at top:
<br>&nbsp;
<blockquote TYPE=CITE>
<pre>A module for reading dbf files. Tested ONLY on linux.

Author&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : Michal Spalinski (mspal@fuw.edu.pl)
Copying policy: unlimited (do what you want with it)
Warranty&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : none whatsoever</pre>
</blockquote>

<p><br>I've hacked it a bit and added some error-checking.&nbsp; If you
improve it greatly, please let me/ the list have the update !
<p>-freddie
<br>to reply, try adding a v as the second letter of my uname.
<br>&nbsp;
<p>Bill Moore wrote:
<blockquote TYPE=CITE>Hi.&nbsp; I'm new to Python and this list.&nbsp;
Has anyone written an interface to dBASE or Clipper tables?
<p>I would like to be able to view field names and browse the contents
of the data file.&nbsp; Optionally, it would be nice to be able to actually
lock a record and modify its contents.
<p>Thanks.
<p>Bill Moore
<p>_______________________________________________
<br>DB-SIG maillist&nbsp; -&nbsp; DB-SIG@python.org
<br><a href="http://mail.python.org/mailman/listinfo/db-sig">http://mail.python.org/mailman/listinfo/db-sig</a></blockquote>

</body>
</html>

--------------FD800CBABF6FC5931B58A796--

--------------70CC157745C8512996C24A0A
Content-Type: text/plain; charset=us-ascii;
 name="dbf.py"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="dbf.py"

# -*- tab-width: 4 -*-
#
# A module for reading dbf files. Tested ONLY on linux. 
#
# Author		: Michal Spalinski (mspal@fuw.edu.pl)
# Copying policy: unlimited (do what you want with it) 
# Warranty		: none whatsoever
#

"""
This is a module for reading dbf files. 

The code fragment

import dbf
db = dbf.dbf('mydata.dbf')

creates a dbf object db associated with an existing dbf file 'mydata.dbf'. 
Then:

-- db.fields returns a a list of tuples describing the fields
-- db.nrecs	 returns the number of records
-- db[n] returns a tuple containing the contents of record number n (0 <= n < nrecs). 
   All field contents are represented as strings ...
-- db.status() prints some data about the dbf file

So, for example, to list the first two fields of all the records in mydata.dbf
one could try:

for r in db: 
	print r[0], r[1]

Good luck!
"""

from struct import unpack 
from string import lower, strip


def int_o_strip(x):
	if strip(x) == "":
		return 0
	else:
		try:
			return int(strip(x))
		except ValueError:
			return float(strip(x))

def float_o_strip(x):
	if strip(x) == "":
		return 0
	else:
		return float(strip(x))

 
class dbf:	
	def __init__(self, fname):
		self.f = open(fname,'rb') 
		head = self.f.read(32) 
		if (head[0] != '\003') and (head[0] != '\203'): 
				raise TypeError, 'Not a Dbase III+ file!' 
		(self.nrecs, self.hlen, self.rlen) = unpack('4xihh20x', head) 
		fdalen = (self.hlen - 33)/32 

		# read the field descriptor array 
		fda = [] 
		for k in range(fdalen): 
				fda.append(self.f.read(32)) 

		# interpret the field descriptors 
		self.fields = [] 
		for fd in fda: 
				bytes = unpack('12c4xBB14x', fd) 
				field = '' 
				for i in range(11): 
					if bytes[i] == '\000': 
						break 
					field = field+bytes[i] 

				type = bytes[11] 
				length = bytes[12] 
				dec = bytes[13] 
				self.fields.append((field,type,length,dec)) 

		self.hook = {}
		
		for i in range(len(self.fields)):
			field, type, length, dec = self.fields[i]
			pos = i
		
			if type == 'N':
				if dec == 0:
					func = int_o_strip
				else:
					func = float_o_strip
			else:
				func = str

			self.hook[lower(field)] = (pos, func, type, length, dec)
			
			
		
 
	# record numbers go from 0 to self.nrecs-1	
	def _get(self, recno): 
		offs = self.hlen + recno*self.rlen 
		self.f.seek(offs,0) 
		return self.f.read(self.rlen) 
	 
	def __getitem__(self, recno):
		if recno < 0 or recno >= self.nrecs:
				raise IndexError, 'No such record.'
		else:
			raw = self._get(recno) 
			res, pos = [], 0 
			for field in self.fields: 
				end = pos+field[2] 
				item = raw[pos+1:end+1] 
				pos=end 
				res.append(item) 
			# return tuple(res)
			return row(tuple(res), 
					   self.hook)

		def status(self):
			print ''
			print 'Header length	 :', self.hlen
			print 'Record length	 :', self.rlen
			print 'Number of records :',  self.nrecs
			print ''
			print '%-12s %-12s %-12s %-12s' % ('Field','Type','Length','Decimal')
			print '%-12s %-12s %-12s %-12s' % ('-----','----','------','-------')
			for k in self.fields:
					print '%-12s %-12s %-12s %-12s' % k
			print ''




class row:
	
	def __init__(self, ptuple=None, hook=None):
		if ptuple:
			self.data = ptuple
		else:
			raise TypeError, 'pas de row vide !'
		if hook:
			self.hook = hook
		else:
			self.hook = None

	def __repr__(self): 
		return `self.data`

	def __len__(self):
		return len( self.data)

	def __getitem__(self, i):
		return self.data[i]

	def __setitem__(self, i, item):
		raise TypeError, 'immutable row so far'

	def __delitem__(self, i, item):
		raise TypeError, 'immutable row so far'

	def __getattr__(self, aname):
		if not self.hook:
			raise TypeError, 'need a hook'

		if not self.hook.has_key(lower(aname)):
			raise ValueError, ('"%s": no such field !' % aname)
		
		pos, func, type, len, dec = self.hook[lower(aname)]

		return func(self.data[pos])


--------------70CC157745C8512996C24A0A--