Low-Level Reading of a dBASE .dbf

Share your advanced PureBasic knowledge/code with the community.
JoeC4281
User
User
Posts: 29
Joined: Fri Aug 06, 2021 4:47 pm

Low-Level Reading of a dBASE .dbf

Post by JoeC4281 »

When I purchased PureBasic back in early August of 2021, my purpose was to convert my 32-bit PowerBasic for Windows 8.0 programs and dlls to 64-bit PureBasic.

With the assistance of this forum, I have been able to create my first 64-bit plugin for JPSoftware's Take Command Console.

The next thing I wanted to convert from PowerBasic was the ability to low-level read a dBASE .dbf without any external programs (ODBC, third-party drivers, etc.)

So, here it is.

It's just a simple demo, which I have tried with three different .dbf files on my system.

The comments in the code are for me, so that I can pursue those items at a later time, but if someone wants to make this code better, go for it.

Joe

Code: Select all

EnableExplicit

Global dbf$
Global hndFile.l
Global *MemoryID
Global *RecordBuffer
Global RecordNumber.l
Global RecordOffset.l
Global bytes.l

Structure DBFHeadStruc
  Version.a
  Year.a
  Month.a
  Day.a
  RecordCount.l
  RecordLength.c
  FirstRec.c
EndStructure

Global DBFHead.DBFHeadStruc

; Your .dbf (fullpath + .dbf name)
; dbf$ = "e:\utils\orders.dbf"
; dbf$ = "e:\utils\trans.dbf"
dbf$ = "e:\utils\lottario.dbf"

; TODO: Check For existence of file before proceeding

; Allocate enough memory to get the dbf header information up to and including byte 11
*MemoryID = AllocateMemory(11)
; Returns nonzero if the file was opened successfully and zero if there was an error.
; If #PB_Any was used As the #File parameter then the new generated number is returned on success. 
hndFile = ReadFile(#PB_Any, dbf$)
bytes = ReadData(hndFile, *MemoryID, 11)
;
; ShowMemoryViewer(*MemoryID,10)
;
DBFHead\Version      = PeekA(*MemoryID + 0)
DBFHead\Year         = PeekA(*MemoryID + 1)
DBFHead\Month        = PeekA(*MemoryID + 2)
DBFHead\Day          = PeekA(*MemoryID + 3)
DBFHead\RecordCount  = PeekL(*MemoryID + 4)
DBFHead\FirstRec     = PeekC(*MemoryID + 8)
DBFHead\RecordLength = PeekC(*MemoryID + 10)
; I could have had only one OpenConsole() in my program, but I didn't
If OpenConsole()
  Print("         DBF Name:               ")
  PrintN(dbf$)
  PrintN("Byte     0: Version:             " + Str(DBFHead\Version))
  PrintN("Byte     1: Year:                " + Str(DBFHead\Year))
  PrintN("Byte     2: Month:               " + Str(DBFHead\Month))
  PrintN("Byte     3: Day:                 " + Str(DBFHead\Day))
  PrintN("Byte   4-7: Record Count:        " + Str(DBFHead\RecordCount))
  PrintN("Byte   8-9: First Record Offset: " + Str(DBFHead\FirstRec))
  PrintN("Byte 10-11: Record Length:       " + Str(DBFHead\RecordLength))
  PrintN("")
  CloseConsole()
EndIf

If *MemoryID
  FreeMemory(*MemoryID)
EndIf

; Allocate enough memory for the entire .dbf file.
; As this can be up to 2GB in size, thinking their might be a better way to do this.
; However, what if I wanted to get the last record in a 2GB file?
*MemoryID = AllocateMemory(Lof(hndFile))
*RecordBuffer = AllocateMemory(DBFHead\RecordLength)

; Change this to the record number of your choosing.
RecordNumber = 16
If RecordNumber <= DBFHead\RecordCount
  RecordOffset = ((RecordNumber - 1) * DBFHead\RecordLength) + DBFHead\FirstRec
  FileSeek(hndFile, RecordOffset)
  Debug "Position: " + Str(Loc(hndFile))

  If *RecordBuffer
    bytes = ReadData(hndFile, *RecordBuffer, DBFHead\RecordLength)
    Debug PeekS(*RecordBuffer, DBFHead\RecordLength, #PB_Ascii)
    ; I could have had only one OpenConsole() in my program, but I didn't
    If OpenConsole()
      PrintN("Record Number: " + Str(RecordNumber))
      PrintN(PeekS(*RecordBuffer, DBFHead\RecordLength, #PB_Ascii))
      CloseConsole()
    EndIf
    FreeMemory(*RecordBuffer)
  EndIf
Else
  ; I could have had only one OpenConsole() in my program, but I didn't
  If OpenConsole()
    PrintN("Record Number exceeds Record Count")
    CloseConsole()
  EndIf
EndIf
  
If *MemoryID
  FreeMemory(*MemoryID)
EndIf

CloseFile(hndFile)
infratec
Always Here
Always Here
Posts: 6810
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Low-Level Reading of a dBASE .dbf

Post by infratec »

Years ago I wrote already a complete toolset for accessing foxpro dbf files.
In fact they are dbase files, but the index stuff is 'extended'.
But I was not able to use the index files :cry:

I have to ask if I'm allowed to provide this code to the public.
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5342
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: Low-Level Reading of a dBASE .dbf

Post by Kwai chang caine »

Apparently works here :D
Can be usefull but like i not have DBF for the moment i use two random file take on internet
https://github.com/infused/dbf/tree/mas ... ntacts.dbf
https://github.com/infused/dbf/blob/mas ... ase_31.dbf
And for the second i have this return
DBF Name: D:\Temp\dbase_31.dbf
Byte 0: Version: 49
Byte 1: Year: 2
Byte 2: Month: 8
Byte 3: Day: 2
Byte 4-7: Record Count: 77
Byte 8-9: First Record Offset: 648
Byte 10-11: Record Length: 95

Record Number: 16
Thanks for sharing 8)
ImageThe happiness is a road...
Not a destination
Post Reply