So there I was, chatting with okasvi and Fangles in the IRC channel. oksavi was trying to come up with a kind of database program to store emails (ignoring duplicates) and then later outputting emails based on certain a passed partial email. Fangles gave him very good advice on different databases and some links on SQL code, etc... but I thought it might be fun to try a simple binary file. No need for any external dependencies or other things like that. A simple solution. Surely not as fast or as easy but it allows you to control what you want.
The code below is my own attempt. An hour's worth of work and my first time ever in trying to create a binary file database. So forgive the poor code ^_^ Also, I did not implement error checking or comments since I wanted to do this quickly for okasvi to check out.
Code: Select all
;- Constants
#Length_Field01 = 50
#Length_Field02 = 50
#Length_Field03 = 50
;- Enumerations
;- Structures
;- Globals
Global FileDB.s : FileDB = "Test.db"
;- Procedures
Procedure xDB_ExportMatch(PartialEmail.s, FileName.s)
;
;
jFile = CreateFile(#PB_Any, FileName)
;
Position = #Length_Field01 + #Length_Field02
;
*HoldMemory = AllocateMemory(#Length_Field03)
;
iFile = ReadFile(#PB_Any, FileDB)
;
If iFile
;
FileSeek(iFile,Position)
;
While Position < Lof(iFile)
;
ReadData(iFile,*HoldMemory, #Length_Field03)
;
If FindString(PeekS(*HoldMemory), PartialEmail, 1)
;
WriteStringN(jFile,PeekS(*HoldMemory))
;
EndIf
;
Position + #Length_Field01 + #Length_Field02 + #Length_Field03
;
FileSeek(iFile,Position)
;
Wend
;
CloseFile(iFile)
;
EndIf
;
If jFile : CloseFile(jFile) : EndIf
;
EndProcedure
Procedure xDB_AddRecord(User.s, Domain.s, Email.s)
;
;
EmailExists.b
;
Position = #Length_Field01 + #Length_Field02
;
HoldSize = Len(Email)
;
*HoldMemory = AllocateMemory(#Length_Field03)
;
iFile = OpenFile(#PB_Any, FileDB)
;
If iFile
;
FileSeek(iFile,Position)
;
While Position < Lof(iFile)
;
ReadData(iFile,*HoldMemory, #Length_Field03)
;
If PeekS(*HoldMemory, HoldSize) = Email : EmailExists = #True : Break : EndIf
;
Position + #Length_Field01 + #Length_Field02 + #Length_Field03
;
FileSeek(iFile,Position)
;
Wend
;
If EmailExists = #False
;
FileSeek(iFile,Lof(iFile))
;
User = LSet(User, #Length_Field01)
;
Domain = LSet(Domain, #Length_Field02)
;
Email = LSet(Email, #Length_Field03)
;
WriteData(iFile,@User, #Length_Field01)
;
WriteData(iFile,@Domain, #Length_Field02)
;
WriteData(iFile,@Email, #Length_Field03)
;
EndIf
;
CloseFile(iFile)
;
EndIf
;
EndProcedure
Procedure xdb_ImportEmails(FileName.s)
;
;
HoldEmail.s
;
EmailExists.b
;
HoldEmptyUser.s = Space(#Length_Field01)
HoldEmptyDomain.s = Space(#Length_Field02)
;
*HoldMemory = AllocateMemory(#Length_Field03)
;
iFile = OpenFile(#PB_Any, FileDB)
;
jFile = ReadFile(#PB_Any, FileName)
;
;
;
If iFile
;
;
While Loc(jFile) < Lof(jFile)
;
HoldEmail = ReadString(jFile)
;
HoldSize = Len(HoldEmail)
;
Position = #Length_Field01 + #Length_Field02
;
EmailExists = #False
;
;
FileSeek(iFile,0)
;
While Position < Lof(iFile)
;
ReadData(iFile,*HoldMemory, #Length_Field03)
;
If PeekS(*HoldMemory, HoldSize) = HoldEmail : EmailExists = #True : Break : EndIf
;
Position = Position + #Length_Field01 + #Length_Field02 + #Length_Field03
;
FileSeek(iFile,Position)
;
Wend
;
If EmailExists = #False
;
HoldEmail = LSet(HoldEmail, #Length_Field03)
;
FileSeek(iFile,Lof(iFile))
;
WriteData(iFile,@HoldEmptyUser, #Length_Field01)
;
WriteData(iFile,@HoldEmptyDomain, #Length_Field02)
;
WriteData(iFile,@HoldEmail, #Length_Field03)
;
EndIf
;
PositionRead + HoldSize
;
;
iCount + 1
;
Wend
;
CloseFile(iFile)
;
EndIf
;
CloseFile(jFile)
;
EndProcedure
;
xdb_ImportEmails("emails.txt")
;
MessageRequester("Emails", "Done Importing", #PB_MessageRequester_Ok)
;
xDB_ExportMatch(".com", "test-out.txt")
;
Then, it seaches our database for any emails containing '.com' and outputs the email.
It'd be pretty simple to change this up and make it a lot more friendly. For simplicity sake we're assuming the three string fields are no more than 50 characters long and we're forcing them to use that amount of space no matter what. Extra bloat, unfortunately, but I didn't want to try and implement the alternative at the moment.
The alternative (one of them) would be to have four fields instead of three. The first would be a long containing the length of the record. That way, you know how long the whole record is and how far you need to skip until you get to the next one. You'd have to handle splitting the fields another way if you did this.
You could also add in some extra functions pretty easily. It'd be simple to make a function to return a record based on it's row number, for example. Or do whatever you like to make it more database-y.

If nothing else, perhaps it'll serve as an example for some other code for you.