commit-gnue
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[gnue] r7509 - trunk/gnue-common/src/utils


From: johannes
Subject: [gnue] r7509 - trunk/gnue-common/src/utils
Date: Mon, 2 May 2005 07:24:39 -0500 (CDT)

Author: johannes
Date: 2005-05-02 07:24:38 -0500 (Mon, 02 May 2005)
New Revision: 7509

Added:
   trunk/gnue-common/src/utils/dbf.py
Modified:
   trunk/gnue-common/src/utils/
Log:
Implemented our own dBase (XBase) data access driver [read only version]



Property changes on: trunk/gnue-common/src/utils
___________________________________________________________________
Name: svn:ignore
   - *.pyc

   + foobar.dbf
*.pyc


Added: trunk/gnue-common/src/utils/dbf.py
===================================================================
--- trunk/gnue-common/src/utils/dbf.py  2005-05-01 12:50:59 UTC (rev 7508)
+++ trunk/gnue-common/src/utils/dbf.py  2005-05-02 12:24:38 UTC (rev 7509)
@@ -0,0 +1,288 @@
+# GNU Enterprise Common Library - Utilities - DBase driver
+#
+# Copyright 2001-2005 Free Software Foundation
+#
+# This file is part of GNU Enterprise
+#
+# GNU Enterprise is free software; you can redistribute it
+# and/or modify it under the terms of the GNU General Public
+# License as published by the Free Software Foundation; either
+# version 2, or (at your option) any later version.
+#
+# GNU Enterprise is distributed in the hope that it will be
+# useful, but WITHOUT ANY WARRANTY; without even the implied
+# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+# PURPOSE. See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public
+# License along with program; see the file COPYING. If not,
+# write to the Free Software Foundation, Inc., 59 Temple Place
+# - Suite 330, Boston, MA 02111-1307, USA.
+#
+# $Id$
+
+import struct
+import datetime
+import types
+import os
+
+# =============================================================================
+# Exceptions
+# =============================================================================
+
+class InvalidFormatError (Exception):
+  def __init__ (self, message):
+    Exception.__init__ (self, "Not a valid DBF file: %s" % message)
+
+
+# =============================================================================
+# Class implementing read-only access to dBase files
+# =============================================================================
+
+class dbf:
+
+  _SIGNATURES = { 2: 'FoxBase',
+                  3: 'File without DBT',
+                  4: 'dBase IV w/o memo file',
+                  5: 'dBase V w/o memo file',
+                 48: 'Visual FoxPro with DBC',
+                 49: 'Visual FoxPro with AutoIncrement field',
+                 67: '.dbv memo var size (Flagship)',
+                123: 'dBase IV with memo',
+                131: 'dBase III with memo file',
+                139: 'dBase IV with memo file',
+                142: 'dBase IV with SQL table',
+                179: '.dbv and .dbt memo (Flagship)',
+                229: 'Clipper SIX driver with SMT memo file',
+                245: 'FoxPro with memo file',
+                251: 'FoxPro'}
+
+  # ---------------------------------------------------------------------------
+  # Create a new wrapper around the given dbase file
+  # ---------------------------------------------------------------------------
+
+  def __init__ (self, filename):
+
+    self.__filename = filename
+    self.__file     = open (filename, 'rb')
+    self.__size     = os.stat (filename).st_size
+
+    self.fields     = []
+    self.signature  = None
+    self.lastUpdate = None
+    self.headerLen  = 0
+    self.recordLen  = 0
+    self.incompleteTransaction = False
+    self.encrypted             = False
+    self.mdxFlag        = None
+    self.languageDriver = None
+
+    self.__version    = None
+    self.__cache      = []
+
+    self.__readHeader ()
+    self.__readFieldDescriptorArray ()
+    self.__readRecords ()
+
+
+  # ---------------------------------------------------------------------------
+  # Read and parse the header of the file
+  # ---------------------------------------------------------------------------
+
+  def __readHeader (self):
+
+    self.__file.seek (0, 0)
+
+    self.__version = struct.unpack ('<B', self.__file.read (1)) [0]
+    if not self.__version in self._SIGNATURES:
+      raise InvalidFormatError, "wrong signature"
+
+    self.signature = self._SIGNATURES [self.__version]
+
+    self.lastUpdate   = struct.unpack ('<3B', self.__file.read (3))
+    self.__numRecords = struct.unpack ('<L', self.__file.read (4)) [0]
+    self.headerLen    = struct.unpack ('<H', self.__file.read (2)) [0]
+    self.recordLen    = struct.unpack ('<H', self.__file.read (2)) [0]
+
+    # Logical file size must match the real file size
+    if 1 + self.headerLen + self.__numRecords * self.recordLen != self.__size:
+      raise InvalidFormatError, "wrong file size"
+
+    reserved = self.__file.read (2)
+
+    self.incompleteTransaction = self.__file.read (1) == '\x01'
+    self.encrypted             = self.__file.read (1) == '\x01'
+
+    freeRecordThread = self.__file.read (4)
+    reserved         = self.__file.read (8)
+
+    self.mdxFlag        = struct.unpack ('<B', self.__file.read (1)) [0]
+
+    # TODO: Add support for codepages (as specified by languageDriver), and
+    #       have all field-values beeing unicode.
+    self.languageDriver = self.__file.read (1)
+
+    reserved = self.__file.read (2)
+
+    self.__file.seek (self.headerLen - 1, 0)
+    if self.__file.read (1) != '\x0d':
+      raise InvalidFormatError, "Missing terminator flag"
+
+    self.dataOffset = self.headerLen
+    # For Visual FoxPro with DBC we have to skip the database container
+    if self.__version == '\x30':
+      self.dataOffset += 263
+
+
+  # ---------------------------------------------------------------------------
+  # Read and parse the field descriptor array starting at offset 20h
+  # ---------------------------------------------------------------------------
+
+  def __readFieldDescriptorArray (self):
+
+    self.__file.seek (32, 0)
+    append = self.fields.append
+
+    for fix in range ((self.headerLen - 33) / 32):
+      fieldName = self.__file.read (11).split ('\x00', 1) [0].strip ()
+      fieldType = self.__file.read (1)
+      (addr, fieldLen, decCount) = struct.unpack ('<LBB', self.__file.read (6))
+
+      reserved       = self.__file.read (2)
+      workAreaId     = struct.unpack ('<B', self.__file.read (1)) [0]
+      reserved       = self.__file.read (2)
+      setFieldsFlags = self.__file.read (1)
+      reserved       = self.__file.read (7)
+      hasIndex       = self.__file.read (1) == '\x01'
+
+      append ((fieldName, fieldType, fieldLen, decCount, hasIndex))
+
+    if not self.fields or len (self.fields) > 128:
+      raise InvalidFormatError, "Invalid field count"
+
+
+  # ---------------------------------------------------------------------------
+  # Read all records from the file and put them into the cache sequence
+  # ---------------------------------------------------------------------------
+
+  def __readRecords (self):
+
+    self.__file.seek (self.dataOffset)
+    append = self.__cache.append
+
+    for recordNumber in xrange (self.__numRecords):
+      stream = self.__file.read (self.recordLen)
+
+      # The first byte in the record stream holds the 'deleted flag'
+      if stream [0] == '*':
+        continue
+
+      append (self.__getRecordDict (stream [1:]))
+
+
+  # ---------------------------------------------------------------------------
+  # Convert a given record stream into a dictionary
+  # ---------------------------------------------------------------------------
+
+  def __getRecordDict (self, stream):
+
+    # The given stream starts *after* the deleted flag, so position 0 is the
+    # first byte of the first field.
+
+    cpos   = 0
+    record = {}
+
+    for (fname, ftype, flen, fdec, index) in self.fields:
+      raw = stream [cpos:cpos + flen]
+      cpos += flen
+
+      # Numeric conversion
+      if ftype == 'N':
+        if fdec:
+          value = float (raw.split ('\x00', 1) [0].strip () or 0)
+        else:
+          value = int (raw.split ('\x00', 1) [0].strip () or 0)
+
+      # Booleans
+      elif ftype == 'L':
+        if raw.strip () in ['?', '']:
+          value = None
+        else:
+          value = raw.strip ().lower () in ['t', 'y']
+
+      # Date values
+      elif ftype == 'D':
+        (year, month, day) = map (int, [raw [:4], raw [4:6], raw [6:]])
+        value = datetime.date (year, month, day)
+
+      # FoxPro Integers
+      elif ftype == 'I':
+        value = struct.unpack ('<i', raw) [0]
+
+      # All other types (including character data)
+      else:
+        value = raw
+
+      record [fname] = value
+
+    return record
+
+
+  # ---------------------------------------------------------------------------
+  # Length of the file is equal to the number of records
+  # ---------------------------------------------------------------------------
+
+  def __len__ (self):
+    return len (self.__cache)
+
+
+  # ---------------------------------------------------------------------------
+  # A file is zero if it has no records
+  # ---------------------------------------------------------------------------
+
+  def __nonzero__ (self):
+    return len (self.__cache) > 0
+
+
+  # ---------------------------------------------------------------------------
+  # Return a generator as iterator for all records
+  # ---------------------------------------------------------------------------
+
+  def __iter__ (self):
+
+    for record in self.__cache:
+      yield record
+
+
+  # ---------------------------------------------------------------------------
+  # Add access method via record number (or slices)
+  # ---------------------------------------------------------------------------
+
+  def __getitem__ (self, index):
+    return self.__cache [index]
+
+# =============================================================================
+# Module self test
+# =============================================================================
+
+if __name__ == '__main__':
+
+  dbfile = dbf ('foobar.dbf')
+
+  print "STATs:"
+  print "  Signature:", dbfile.signature
+  print "  # Records:", len (dbfile)
+  print "  Recordlen:", dbfile.recordLen
+  print "  incomplete transactions:", dbfile.incompleteTransaction
+  print "  Encrypted              :", dbfile.encrypted
+  print
+  print "  Fields"
+  for fdef in dbfile.fields:
+    print "    ", fdef
+
+  print
+  print "ROWS:"
+  print 
+
+  for row in dbfile:
+    print "  ", row


Property changes on: trunk/gnue-common/src/utils/dbf.py
___________________________________________________________________
Name: svn:keywords
   + Id





reply via email to

[Prev in Thread] Current Thread [Next in Thread]