(Note: this is being done in Visual Basic 6.0)
What is the Most efficient way of makeing a User Database?
I could store everyting in a single file: usernames, moderator lvl, flags, etc. and use one of the methods discussed in "Most efficient way of saving settings on a bot?" - http://forum.valhallalegends.com/phpbbs/index.php?topic=11769.0
or
I could use a seperate file for each user, useing the user's nick as the file name.
Presuming your program has the capability to cache the results in memory, it won't matter too much which of those methods you go with. However, from a disk usage perspective, saving one file per username is horribly wasteful.
Also, if you really care about efficiency, stop using VB. :) At least upgrade to VB.NET and use the oft-touted support for compiling to MSIL (and from there to native code), which might get you close to performance of a C application if the VB.NET->MSIL generator doesn't do too terrible a job. Our resident .NET geeks can probably give you more advice in this area if you decide to pursue it.
This is how I do it.Option Explicit
Private Type UserList
Username As String
Flags As String
End Type
Private UL() As UserList
Public Users As Integer
Public Sub LoadUsers()
Dim Splt() As String, sInput As String, pfNum As Integer, I As Integer
If Dir$(App.Path & "\Database.dcu") = vbNullString Then
Call MsgBox("Error: User Database could not be found!", vbCritical, "Error!")
Exit Sub
End If
ReDim UL(0)
I = 0
pfNum = FreeFile
Open (App.Path & "\Database.dcu") For Input As #pfNum
Do Until EOF(pfNum)
Input #pfNum, sInput
If Not sInput = vbNullString Then
Splt() = Split(sInput, ":", 1) '// Userlist syntax is "Username:Flags"
UL(I).Username = Splt(0)
If (InStr(Splt(1), "B") <> 0) Then
If (InStr(Splt(1), "L") <> 0) Then
Splt(1) = "BL"
Else
Splt(1) = "B"
End If
ElseIf (InStr(Splt(1), "K") <> 0) Then
If (InStr(Splt(1), "L") <> 0) Then
Splt(1) = "KL"
Else
Splt(1) = "K"
End If
ElseIf (InStr(Splt(1), "Z") <> 0) Then
If (InStr(Splt(1), "L") <> 0) Then
Splt(1) = "ZL"
Else
Splt(1) = "Z"
End If
End If
UL(I).Flags = UCase$(Alphabetize(Splt(1)))
I = I + 1
ReDim Preserve UL(0 To UBound(UL) + 1)
End If
Loop
Close #pfNum
Users = I
End Sub
Let me be the first to say: eww! Even without writing VB, I can point out a few improvements (aside from the obvious one of not using VB, of course).
- Compose your pathname once, not twice. It's not a relevant performance hit to do it twice, but it could become a maintenance issue later if you change one and not the other. Compose it to a temporary variable, then do both your existence test and your open against the contents of that variable.
- Modify the error message to print out the file it sought (and ideally the path also). Expecting the user to guess what to do about a missing database is a bad idea, and that's effectively what your code does. Remember, documentation is only meant to kill trees: we don't want to write it, and users don't want to read it. An error message announcing what is missing and what to do about it (or at least containing a reference on where to find that information) would be helpful.
- Compose your flags in a temporary buffer, rather than trying to filter the existing one. That is, do this (your problem to convert pseudocode to VB):
flags = "";
if (fromFile[1] contains "B")
flags += "B";
if (fromFile[1] contains "L")
flags += "L";
if (fromFile[1] contains "Z")
flags += "Z";
user_record.flags = flags;
My approach is much more extensible, and IMO more readable. Of course, I'd still highly advise going to a binary flags approach internally, but at least get away from enumerating every possible combination of access flags! - Reallocate the record array less often. Consider having it grow in size by z% when needed, and then truncate it down to the minimal size when you're done. For a good choice of z, that should get you down to a half dozen or so reallocations even for a huge list.
I may oneday leave the darkside of the programming force and learn C# or C++ :P
----------------------------------------------------------------------------------------------------------------------------------------------------------
I would think useing a single user defined type with some properties as arrays in a binary file would be better and maby smaller than that
IE: (I'm not shure if the syntax is correct, but..)
public type database
users() as string
IndexOfLastEntrie as integer
flaga() as string
flagb() as string
flagc() as string
flagd() as string
end type
So as to store and grap everything with just one put or get statment and not have to split anything; though, you would have to loop through to find the correct user.
Yet,
Useing one file for each user with the filename being the username would remove the need to loop through; just open put or get a single user defined varable and close.
It is possable that the userdata base could get very large and the method used should be able to access a single user when the database could possably contain 10,000 or more users.- I think that that big of a database would not make it possable to loop through all those users.
I could also use a string varable to store all the usernames with the array position. IE: username,index,username,index,username,index,username,index,username,index
and then use find or somthing to retreave a carater position and then work from there to find the index number
'----------------------------------------------------------------------------
' Module : modDatabase
' DateTime : 5/5/2005 12:44
' Author : Spencer Ragen
' Purpose : Database handling, duh
'----------------------------------------------------------------------------
Option Explicit
Public Type s_DatabaseEntry
UserName As String * 25
AddBy As String * 25
AddOn As String * 8
AddAt As String * 12
Access As Integer
Master As Boolean
End Type
Private DBUsers() As s_DatabaseEntry
Public Sub DBStartup()
ReDim DBUsers(0)
End Sub
Public Sub debugdb()
Dim c As s_DatabaseEntry
Dim i As Integer
For i = LBound(DBUsers) To UBound(DBUsers)
c = DBUsers(i)
Debug.Print c.UserName
Debug.Print c.Master
Debug.Print c.Access
Debug.Print c.AddBy
Debug.Print c.AddOn
Debug.Print c.AddAt & vbNewLine
Next i
End Sub
Public Sub LoadDatabase()
Dim t As String
If varDatabase = "" Or Dir$(varDatabase) = "" Then
AddC vbRed, "Database file is invalid"
End If
Dim c As s_DatabaseEntry
Dim k As String
Open varDatabase For Random As #1
Do While Not EOF(1)
Get #1, , c
If c.UserName <> String(25, vbNullChar) Then
c.UserName = Trim(c.UserName)
c.AddBy = Trim(c.AddBy)
AddDBUser c
End If
Loop
Close #1
If Not IsFound(BotMaster) Then
GetTimeStamp t
c.UserName = LCase(BotMaster)
c.Access = 101
c.Master = True
t = Replace(t, " ", "")
t = Replace(t, "[", "")
c.AddAt = Replace(t, "]", "")
c.AddBy = "#settings"
c.AddOn = Format(Date, "mm.dd.yy")
AddDBUser c
End If
'Ignore this, it's for my bot :\
'If ShowMisc Then AddC colInfo, UBound(DBUsers) + 1 & " database entries loaded"
End Sub
Public Sub SaveDatabase()
Dim i As Integer
Dim o As Integer
Dim ent As String
Dim list() As String
ReDim list(0)
Open varDatabase For Random As #1
For i = LBound(DBUsers) To UBound(DBUsers)
For o = 0 To UBound(list)
If LCase(list(o)) = LCase(DBUsers(i).UserName) Then GoTo SkipSave
Next o
If list(UBound(list)) <> "" Then
ReDim Preserve list(UBound(list) + 1)
End If
list(UBound(list)) = DBUsers(i).UserName
If DBUsers(i).UserName <> Null And DBUsers(i).UserName <> "" Then Put #1, , DBUsers(i)
SkipSave:
Next i
Close #1
End Sub
Public Sub AddDBUser(c As s_DatabaseEntry)
If DBUsers(UBound(DBUsers)).UserName <> "" And DBUsers(UBound(DBUsers)).UserName <> String(25, vbNullChar) Then
ReDim Preserve DBUsers(UBound(DBUsers) + 1)
End If
DBUsers(UBound(DBUsers)) = c
If c.Access >= 30 Or c.Master Then AddSafe c.UserName
End Sub
Public Sub DelDBUser(strName As String)
Dim i As Integer
For i = LBound(DBUsers) To UBound(DBUsers)
If LCase(strName) = Trim(DBUsers(i).UserName) Then
DBUsers(i).UserName = ""
Call SaveDatabase
Exit Sub
End If
Next i
End Sub
Public Function IsFound(strUser As String) As Boolean
Dim i As Integer
For i = LBound(DBUsers) To UBound(DBUsers)
If LCase(strUser) = Trim(DBUsers(i).UserName) Then
IsFound = True
Exit Function
End If
Next i
End Function
Public Function GetDBEntry(strName As String) As s_DatabaseEntry
Dim i As Integer
For i = LBound(DBUsers) To UBound(DBUsers)
If LCase(strName) = Trim(DBUsers(i).UserName) Then
GetDBEntry = DBUsers(i)
Exit Function
End If
Next i
GetDBEntry.UserName = "not found"
End Function
Public Function GetAccess(strName As String) As Integer
Dim i As Integer
For i = LBound(DBUsers) To UBound(DBUsers)
If LCase(strName) = Trim(DBUsers(i).UserName) Then
GetAccess = DBUsers(i).Access
Exit Function
End If
Next i
GetAccess = -1
End Function
What about an application data file? Perhaps a MS Access file? Would that be better than any of the other methods discussed thus far?