|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Prerequisites |
None. |
|
VB's
Random Access file routines provides a powerful way to create well
defined file structures and to access that data without the restrictions
applied to normal sequential
file operations. Random Access files are particularly well suited for
applications that do not require the weight and complexity of a database
back end, such as a personal contact manager or other similar
application.
One of the difficulties encountered in using random access files is the deletion of outdated records. While the conventional and recommended programming practice is to include a flag member in the UDT that defines the file record that can be flipped to indicate that - on the next save - the particular record should be overwritten with the data of a new record. And while this is certainly a recommended practice (as compared with the only other option of writing the existing data to a new file up top the record to be deleted, skipping that record, and continuing to the end of the file), having the ability to delete records from the end of a random access file is not outside the abilities of a few API calls. Random access files - along with the methods to manipulate random access file data - is unique to VB; Windows knows nothing about random access file access, and as such the Windows APIs for writing to files can't manipulate the file's data in the same way that VB can. But Windows does provide a specific API - SetEndOfFile - that can be used against a random access file to truncate one or more records. The demo as coded below does three things:
The structure and layout of the UDT for this demo was made to provide you with an easy-to-see confirmation of the start and end data for each record when printed to the immediate window. Once you've reached the second step, the records will display data similar to:
begin record 1
the
end of 14 When the first truncation takes place, the file will be cut immediately before "begin record 15", and so on for each subsequent press. Given the code and comments, it would be a small task to alter the routine to accept a value that indicated the number of records to delete from the end of the file other than just one, since the number of records to delete times the size of one record is all that would need to be subtracted from the file total size to affect his modification. |
BAS Module Code |
None. |
|
Form Code |
Add three command buttons (Command1 / Command2 / Command3) to a form along with the following code: |
|
Option Explicit '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' Copyright ©1996-2011 VBnet/Randy Birch, All Rights Reserved. ' Some pages may also contain other copyrights by the author. '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' Distribution: You can freely use this code in your own ' applications, but you may not reproduce ' or publish this code on any web site, ' online service, or distribute as source ' on any media without express permission. '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' 'change this to a suitable location on your system! Private Const sTestFile As String = "d:\test.txt" Private Const OPEN_ALWAYS As Long = 4 Private Const FILE_BEGIN As Long = 0 Private Const GENERIC_WRITE As Long = &H40000000 Private Const GENERIC_READ As Long = &H80000000 Private Const FILE_ATTRIBUTE_NORMAL As Long = &H80 'test file UDT Private Type TestData s1 As String * 15 s2 As String * 5 dw1 As Long dt1 As Date dw2 As Long s3 As String * 13 End Type Private Declare Function CreateFile Lib "kernel32" _ Alias "CreateFileA" _ (ByVal lpFileName As String, _ ByVal dwDesiredAccess As Long, _ ByVal dwShareMode As Long, _ ByVal lpSecurityAttributes As Long, _ ByVal dwCreationDisposition As Long, _ ByVal dwFlagsAndAttributes As Long, _ ByVal hTemplateFile As Long) As Long Private Declare Function GetFileSize Lib "kernel32" _ (ByVal hfile As Long, lpFileSizeHigh As Long) As Long Private Declare Function SetFilePointer Lib "kernel32" _ (ByVal hfile As Long, _ ByVal lDistanceToMove As Long, _ lpDistanceToMoveHigh As Long, _ ByVal dwMoveMethod As Long) As Long Private Declare Function SetEndOfFile Lib "kernel32" _ (ByVal hfile As Long) As Long Private Declare Function CloseHandle Lib "kernel32" _ (ByVal hfile As Long) As Long Private Sub Form_Load() Command1.Caption = "Create random file" Command2.Caption = "Read random file data" Command3.Caption = "Delete last random record" End Sub Private Sub Command1_Click() Dim td As TestData Dim nodata As TestData Dim cnt As Long Dim hfile As Long hfile = FreeFile Open sTestFile For Random Access Write As #hfile Len = Len(td) 'add 15 random records to the test file For cnt = 1 To 15 With td 'for debug purposes, this line is 'the first line of each record written .s1 = "begin record " & cnt 'for debug purposes, this line is 'the last line of each record written RSet .s3 = "the end of " & cnt 'some other random data for the record .s2 = String(Len(.s1), cnt + 96) .dw1 = 1234 .dw2 = 3445 .dt1 = Now End With Put #hfile, cnt, td td = nodata Next cnt Close #hfile Debug.Print "file created" End Sub Private Sub Command2_Click() Dim td As TestData Dim nodata As TestData Dim cnt As Long Dim hfile As Long Dim totalrecords As Long hfile = FreeFile Open sTestFile For Random Access Read As #hfile Len = Len(td) totalrecords = LOF(hfile) \ Len(td) For cnt = 1 To totalrecords Get #hfile, cnt, td With td Debug.Print .s1 Debug.Print .s2 Debug.Print .dw1 Debug.Print .dw2 Debug.Print .dt1 Debug.Print .s3 End With td = nodata Next cnt Close #hfile End Sub Private Sub Command3_Click() DeleteLastRandomRecord End Sub Private Sub DeleteLastRandomRecord() Dim td As TestData Dim hfile As Long Dim totalrecords As Long Dim newfilesize As Long Dim dwFileSizeLow As Long Dim dwFileSizeHigh As Long 'how many records in the file? totalrecords = FileLen(sTestFile) \ Len(td) 'can't truncate the file if 'totalrecords is less than 2 If totalrecords >= 2 Then 'open the file using the API hfile = CreateFile(sTestFile, _ GENERIC_WRITE Or GENERIC_READ, _ 0&, ByVal 0&, _ OPEN_ALWAYS, _ FILE_ATTRIBUTE_NORMAL, _ 0&) 'Get the file size. Note that the value 'returned is the size of the file as 'stored in the low word of GetFileSize with 'the high word returning 0. This will 'accommodate most reasonably-sized files. 'If the file is large, however, then the low 'and high word values have to be be combined 'to get the actual size. This demo assumes a 'you're using a reasonably sized random access 'file, so a test of dwFileSizeHigh is made 'to ensure it is 0 before truncating. dwFileSizeLow = GetFileSize(hfile, dwFileSizeHigh) If dwFileSizeHigh = 0 Then 'dwFileSizeLow for this test is the 'file size; one record is Len(td), 'so in order to delete the last 'record the new size is the current 'size minus the length of one record: newfilesize = dwFileSizeLow - Len(td) Debug.Print totalrecords, dwFileSizeLow, newfilesize 'now move the file pointer to the 'new end position .. If SetFilePointer(hfile, newfilesize, dwFileSizeHigh, FILE_BEGIN) > 0 Then 'and "delete" the last record by 'changing the end of file marker Call SetEndOfFile(hfile) End If 'SetFilePointer End If 'dwFileSizeHigh CloseHandle hfile End If End Sub |
Comments |
|
|
|
|||||
|
|||||
|
|||||
Copyright ©1996-2011 VBnet and Randy Birch. All Rights Reserved. |