stvz

demdbf.py

Sep 11th, 2013
124
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 10.28 KB | None | 0 0
  1. #!/usr/bin/env python
  2. # -*- coding: UTF-8 -*
  3. """
  4. Library for work with DBF files (without iterators for work with old Python versions)
  5. """
  6. # Changelog:
  7. # 0.2 - 2005-09-22
  8. #                  line 128 and late - for NUMERIC - count of the number of decimal places
  9. # 0.3 - 2006.05.31
  10. #                Add type 0x30    Visual FoxPro
  11. #                If field type not = \000 ignore this field (for DBF with empty headers)
  12. VERSION=0.2
  13.  
  14. from struct import unpack,pack
  15. import string
  16. import re
  17.  
  18. class dbf:
  19.       """DBF header"""
  20.  
  21.       def __init__(self):
  22.           self.dbfType       = 0  # Type of file
  23.           self.lastChange    = "" # Last change date
  24.           self.recordCount   = 0  # Field count
  25.           self.recordsOffset = 0  # location start of data
  26.           self.recordLength  = 0  # Record length
  27.           self.haveCDX       = 0  # Have or not index file CDX
  28.           self.fields        = []
  29.           self.filename      = ''
  30.           self.fh            = None
  31.           self.recormask     = "" # mask fro split row into fields
  32.  
  33.       def cloneDBF(self,dest):
  34.           """ Clone struct DBF with dest descriptors """
  35.           hdr = pack("cccciHH16sc3s",chr(self.dbfType),
  36.                                    chr(int(self.lastChange[:2])),
  37.                                    chr(int(self.lastChange[2:4])),
  38.                                    chr(int(self.lastChange[4:])),
  39.                                    0,
  40.                                    self.recordsOffset,
  41.                                    self.recordLength,
  42.                                    chr(0x00)*16,
  43.                                    chr(self.haveCDX),
  44.                                    chr(0x00)*3)
  45.           dest.write(hdr)
  46.           # Write fields
  47.           for f in self.fields:
  48.               name = f[0]+chr(0x00)
  49.               try:
  50.                 record = pack("11sclBB14x",name,f[1],0,f[2],f[3])
  51.               except:
  52.                         print "error in",name,f[1],0,f[2],f[3]
  53.                         raise
  54.               if len(record)<32:
  55.                  record = record+(chr(0x00)*(32-len(record)))
  56.               dest.write(record)
  57.           dest.write(chr(0x0D))
  58.           #Не забыть добавить призрачную запись.
  59.           dest.write(chr(0x20)*self.recordLength)
  60.  
  61.       def close(self):
  62.           """ Close DBF """
  63.           self.fh.close()
  64.  
  65.       def delete(self,recno):
  66.           """ Delete record with recno number"""
  67.           if recno > self.recordCount:
  68.             raise IndexError
  69.           self.fh.seek(self.recordsOffset+(recno*self.recordLength))
  70.           print "Current pos:",self.fh.tell()
  71.           self.fh.write(chr(0x2A)) # first field is delete flag
  72.           print "Write:",chr(0x2A)
  73.  
  74.       def open(self,filename):
  75.           """ Open file """
  76.           self.filename=filename
  77.           self.fh = open(filename,"rb")
  78.           self.fh.seek(0)
  79.           self.fromFile()
  80.           # generate records mask for parsing
  81.           mask = "1s" # delete field
  82.           for f in self.fields:
  83.               mask=mask+str(f[2])+"s"
  84.           self.recormask = mask
  85.  
  86.       def appendRaw(self,raw):
  87.           """ Add new 'raw' record """
  88.           # Go to end
  89.           self.fh.seek(self.recordsOffset+((self.recordCount)*self.recordLength))
  90.           self.fh.write(raw)
  91.           # add ghost record
  92. #          self.fh.write(chr(0x20)*self.recordLength)
  93.           # Renew header (row count)
  94.           self.recordCount+=1
  95.           self.fh.seek(4)
  96.           self.fh.write(pack("i",self.recordCount))
  97.  
  98.       def append(self,data):
  99.           """ Add record"""
  100.           self.appendRaw(self.hache2raw(data))
  101.  
  102.       def raw2hache(self,raw,needFields=None,result={}):
  103.           """ Parse 'raw' row in dict
  104.          Convert  N type into int or float dec count depending
  105.          Convert onli fields including into needFields
  106.          """
  107.           record = unpack(self.recormask,raw)
  108.           result['_DELETE'] = record[0]
  109.  
  110.           for i in xrange(len(record)-1):
  111.             fn = self.fields[i][0]
  112.             if not needFields or fn in needFields:
  113.               if self.fields[i][1]=='N':
  114.                  if self.fields[i][3]==0:
  115.                     res=0
  116.                     try:
  117.                        res=int(record[i+1].strip())
  118.                     except:
  119.                        pass #
  120.                  else:
  121.                     res=0.00
  122.                     try:
  123.                        res=float(record[i+1].strip())
  124.                     except:
  125.                        pass #
  126.                  result[fn] = res
  127.               else:
  128.                  result[fn] = record[i+1].strip()
  129.           return result
  130.  
  131.       def hache2raw(self,data):        
  132.           """ Convert dict into row """
  133.           params = [self.recormask,chr(0x20)] # Mask and DELETE.
  134.           for f in self.fields:
  135.               if f[1]=='C':
  136.                         params.append(("%-"+str(f[2])+"s") % str(data.get(f[0],'')))
  137.               elif f[1]=='N' and f[3]>0:
  138.                         try:
  139.                              params.append(("%"+str(f[2])+"."+str(f[3])+"f") % float(data.get(f[0],'')))
  140.                         except:
  141.                              print "Error in",f
  142.                              raise
  143.  
  144.               else:
  145.                         params.append(("%"+str(f[2])+"s") % str(data.get(f[0],''))) # выровнять по правому краю
  146.           return apply(pack,params)
  147.  
  148.       def write(self,recno,data):
  149.           """ Write data from # position """
  150.           self.writeRaw(recno,self.hache2raw(data))
  151.  
  152.       def read(self,recno,needFields=None):
  153.           """Read record recno"""
  154.           return self.raw2hache(self.readRaw(recno),needFields).orderedDict
  155.  
  156.       def readRaw(self,recno):
  157.           """ Read one record in raw mode """
  158.           if recno > self.recordCount:
  159.             raise IndexError
  160.           self.fh.seek(self.recordsOffset+(recno*self.recordLength))
  161.           return self.fh.read(self.recordLength)
  162.  
  163.       def writeRaw(self,recno,rawdata):
  164.           """ Change record writing radata in his place
  165.          WARNING size of rawdata not checking """
  166.           if recno > self.recordCount:
  167.             raise IndexError
  168.           self.fh.seek(self.recordsOffset+(recno*self.recordLength))
  169.           self.fh.write(rawdata)
  170.  
  171.       def getNext(self,recno):
  172.           """  Return next not deleted row
  173.            Data returning in (num,row) form
  174.            if not found row return None
  175.          """
  176.           while 1:
  177.                 recno = recno+1
  178.                 if recno>self.recordCount:
  179.                    return None
  180.                 else:
  181.                  data =self.readRaw(recno)
  182.                 if len(data)<self.recordLength:
  183.                    break
  184.                 if data[0]!=chr(0x2A):  
  185.                    return recno,data
  186.           return None
  187.  
  188.       def toFile(self,fh): #Пока не закончено.
  189.           """ Write header to file. File must be open into
  190.          write with binary mode. Write offset must be 0"""
  191.           hdr = pack("cccciHH16sc3s",chr(self.dbfType),
  192.                                    chr(int(self.lastChange[:2])),
  193.                                    chr(int(self.lastChange[2:4])),
  194.                                    chr(int(self.lastChange[4:])),
  195.                                    self.recordCount,
  196.                                    self.recordsOffset,
  197.                                    self.recordLength,
  198.                                    chr(0x00)*16,
  199.                                    chr(self.haveCDX),
  200.                                    chr(0x00)*3)
  201.           fh.write(hdr)
  202.           # Пишем поля
  203.           for f in self.fields:
  204.               name = f[0]+chr(0x00)
  205.               record = pack("11sclbb14x",name,f[1],0,f[2],f[3])
  206.               print "Record length:",len(record)
  207.               if len(record)<32:
  208.                  record = record+(chr(0x00)*(32-len(record)))
  209.               fh.write(record)
  210.           fh.write(chr(0x0D))
  211.           #Add ghost record.
  212.           fh.write(chr(0x20)*self.recordLength)
  213.                                    
  214.       def fromFile(self):
  215.           """ read header from file """
  216.           mask = "c3ciHH16xc3x"
  217.           (self.dbfType, year,month,day,self.recordCount,self.recordsOffset,self.recordLength,self.haveCDX) = unpack(mask,self.fh.read(32))
  218.           self.lastChange = "%02i%02i%02i" % (ord(year),ord(month),ord(day))
  219.           self.haveCDX = ord(self.haveCDX)
  220.           self.dbfType = ord(self.dbfType)
  221.           # Read fields description
  222.           self.fields = []
  223.           for i in range((self.recordsOffset-33)/32):
  224.               bytes = unpack('12c4xBb14x', self.fh.read(32))
  225.               field = ''
  226.               for i in xrange(11):
  227.                   if bytes[i] == '\000':
  228.                       break
  229.                   field = field+bytes[i]
  230.               type = bytes[11]
  231.               if type.strip()!='\000':
  232.                       length = bytes[12]
  233.                       dec = bytes[13]
  234.                       self.fields.append((field,type,length,dec))
  235.  
  236.       def printInfo(self):
  237.           if self.dbfType==0x03:
  238.              dbtype = "FoxBASE+/dBASE III +, без memo"
  239.           elif self.dbfType==0x83:
  240.              dbtype = "FoxBASE+/dBASE III + c мемо"
  241.           elif self.dbfType==0x03:
  242.              dbtype = "FoxPro/dBASE IV, без memo"
  243.           elif self.dbfType==0xF5:
  244.              dbtype = "FoxPro с memo"
  245.           elif self.dbfType==0x8B:
  246.              dbtype = "dBASE IV с memo"
  247.           elif self.dbfType==0x30:
  248.              dbtype = "Visual FoxPro"
  249.           else:
  250.              dbtype = "Format unknown "+hex(self.dbfType)
  251.           print "Format:...................",dbtype
  252.           #
  253.           print "Last change time:.........",self.lastChange #
  254.           #
  255.           print "Record count:.............",self.recordCount
  256.          
  257.           print "First record offset:......",self.recordsOffset
  258.           #
  259.           print "One record length:........",self.recordLength
  260.           print "1-exist (CDX type),0-not..",self.haveCDX
  261.           print "Fields:"
  262.           print "Name...... Typ Len...Dec"
  263.           print "------------------------"
  264.           for i in self.fields:
  265.               print "%-10s %3s %4i %3i" % i
Advertisement
Add Comment
Please, Sign In to add comment