'DBF to CSV text converter
'Program written by Steve McNeill @ 9/19/2012
'Code is free to use, abuse, modify, destroy, steal, copy, share, and alter in any way anyone wishes.
'Just be aware, I'm not responsible if it melts your computer, fries your brain, or makes you sing like a drunken sailor.
'Use is purely at your own risk, but it seems safe enough to me!
'All this does is convert old dbf files into a simple CSV text file, which can then be read into any program which you wish to use the data with.
'Your old files stay as they are, and it does nothing to them except read them and then give you a new, converted file to work with.
'change file$ and file1$ to the name of your DBF and new converted filename, respectively.
'No credit, cash, check, or money order needed for this. Enjoy!!
TYPE DBF_HeaderTerminator
EndCode
AS _UNSIGNED _BYTE 'Our End of Field Code is a CHR$(13), or 13 if we read it as a byte
DIM DataFS
(1) AS Field_Subrecord
DIM DataHT
AS DBF_HeaderTerminator
DIM DataVFP
AS DBF_VFPInfo
file$ = ".\tempdata.dbf"
file2$ = ".\converted.txt"
Get_Header file$, DataH
'Display_Header DataH
Get_Fields file$, DataFS()
'Display_Fields DataFS()
Print_Data file$, DataH, DataFS(), file2$
PRINT "Your file has been converted." PRINT "The original file was: "; file$
PRINT "The converted file is: "; file2$
SUB Display_Header
(DataH
AS DBF_Header
) PRINT "Data File Type: ";
CASE 3:
PRINT "FoxBASE+/Dbase III plus, no memo" CASE 49:
PRINT "Visual FoxPro, autoincrement enabled" CASE 50:
PRINT "Visual FoxPro with field type Varchar or Varbinary" CASE 67:
PRINT "dBASE IV SQL table files, no memo" CASE 99:
PRINT "dBASE IV SQL system files, no memo" CASE 131:
PRINT "FoxBASE+/dBASE III PLUS, with memo" CASE 203:
PRINT "dBASE IV SQL table files, with memo" CASE 229:
PRINT "HiPer-Six format with SMT memo file" CASE 245:
PRINT "FoxPro 2.x (or earlier) with memo" PRINT "Date: "; DataH.Month;
"/"; DataH.Day;
"/"; DataH.Year
PRINT "Number of Records: "; DataH.RecordNumber
PRINT "First Record: "; DataH.FirstRecord
PRINT "Record Length: "; DataH.RecordLength
PRINT "Reserved Junk: "; DataH.ReservedJunk
none = 0
IF DataH.TableFlag
AND 1 THEN PRINT "file has a structural .cdx ";: none
= -1 IF DataH.TableFlag
AND 2 THEN PRINT "file has a Memo field ";: none
= -1 IF DataH.TableFlag
AND 4 THEN PRINT "file is a database (.dbc) ";: none
= -1 PRINT "Code Page Mark: "; DataH.CodePageMark
PRINT "Reserved Junk: "; DataH.ReservedJunk1
SUB Display_Fields
(DataH
() AS Field_Subrecord
) PRINT "Field Name :"; DataH
(r
).FieldName
PRINT "Field Type :"; DataH
(r
).FieldType
PRINT "Field Displacement :"; DataH
(r
).Displacement
PRINT "Field Length :"; DataH
(r
).FieldLength
PRINT "Field Decimal :"; DataH
(r
).FieldDecimal
PRINT "Field Flags :"; DataH
(r
).FieldFlags
PRINT "Field AutoNext :"; DataH
(r
).AutoNext
PRINT "Field SutoStep :"; DataH
(r
).AutoStep
PRINT "Field Reserved Junk :"; DataH
(r
).ReservedJunk
PRINT "**************************"
SUB Get_Header
(file$
, DataH
AS DBF_Header
)
SUB Get_Fields
(file$
, DataH
() AS Field_Subrecord
) DIM temp
AS Field_Subrecord
counter = -1: s = 33
counter = counter + 1
s = s + 32
REDIM DataH
(counter
) AS Field_Subrecord
GET #1, 32 * r
+ 1, DataH
(r
) 'record 1 is our header info, so we need to start our field info at record 2
SUB Print_Data
(file$
, DataH
AS DBF_Header
, DataFS
() AS Field_Subrecord
, file2$
) SEEK #1, DataH.FirstRecord
+ 1 GET #1, , databyte
'This is the first byte which tells us if the record is good, or has been deleted. 'C is for Characters, or basically STRING characters.
'0 is for Null Flags, which I have no clue what they're for. I'm basically reading them here as worthless characters until I learn otherwise.
temp$ = ""
FOR j
= 1 TO DataFS
(i
).FieldLength
temp$
= temp$
+ CHR$(databyte
) 'Y is for currency, which is an _INTEGER 64, with an implied 4 spaces for decimal built in.
'N is for numberic, F is for Floating numbers, and both seem to work in the same manner.
'M is for Memo's, which are stored in a different DBT file. What we have here is the block number of the memo location in that file, stored as a simple set of characters.
'G is for OLE files. We store the info for it just the same as we do for a Memo.
'we read the whole thing as a string, which is an odd way for dBase to write it, but I don't make the rules. I just convert them!
temp$ = ""
FOR j
= 1 TO DataFS
(i
).FieldLength
temp$
= temp$
+ CHR$(databyte
) 'D is for Date fields.
'Dates are stored as a string, in the format YYYYMMDD
temp$ = ""
FOR j
= 1 TO DataFS
(i
).FieldLength
temp$
= temp$
+ CHR$(databyte
) month$
= MID$(temp$
, 5, 2) temp$ = day$ + "/" + month$ + "/" + year$
'L is our logical operator. Basically, it's simply True or False Boolean logic
IF databyte
= 32 THEN temp$
= "True" ELSE temp$
= "false" '@ are Timestamps, which I'm too lazy to fully support at the moment.
'They are 8 bytes - two longs, first for date, second for time.
'The date is the number of days since 01/01/4713 BC.
'Time is hours * 3600000L + minutes * 60000L + Seconds * 1000L
'All I'm going to do is read both longs as a single _Integer64 and then write that data to the disk.
'Be certain to convert it as needed to make use of the Timestamp.
'I'm just lazy and don't wanna convert anything right now! :P
'O are double long integers -- basically Integer 64s. Since I'm reading a timestamp as an Int64, this routine works for them as well.
'Long Integers. Basically 4 byte numbers
'+ are auto-increments. Stored the same way as a Long.