dcsimg
Results 1 to 9 of 9

Thread: [RESOLVED] "string * x" with random access files

  1. #1

    Thread Starter
    Lively Member ahmedcrow's Avatar
    Join Date
    Jul 2017
    Posts
    82

    Resolved [RESOLVED] "string * x" with random access files

    I typed this new program as a test to understand a problem in saving and loading data into and from random access file. I noticed that the program works good if I didn't define a size for a string variable and didn't use "len=" in the "open" statement, why this happen?!

    Code:
    Dim varFileHandle As Integer
    Dim varRecSize As Long
    Dim varString As String
    
    Private Sub Command1_Click()
    Close
    End
    End Sub
    
    Private Sub Command2_Click()
    
    Let varFileHandle = FreeFile
    Let varRecSize = Len(varString)
    
    
    Open App.Path & "\data.dat" For Random As varFileHandle 'Len = varRecSize
    
    Get varFileHandle, 2, varString
    
    Close varFileHandle
    
    MsgBox varString
    
    End Sub
    
    Private Sub Form_Load()
    
    Let varFileHandle = FreeFile
    Let varRecSize = Len(varString)
    
    If Not Dir(App.Path & "\data.dat") = "" Then
    Kill (App.Path & "\data.dat")
    End If
    
    Open App.Path & "\data.dat" For Random As varFileHandle 'Len = varRecSize
    
    Put varFileHandle, , "Hello"
    Put varFileHandle, , "World!"
    
    Close varFileHandle
    
    End Sub

  2. #2
    PowerPoster
    Join Date
    Feb 2006
    Posts
    19,074

    Re: "string * x" with random access files

    VB's file numbers are not "handles" and calling them that leads to confusion, errors, and frustration.

    Naming every variable "varXXX" is sort of silly. What next, name every function "funcXXX" and every subroutine "subXXX" and so on?

    If you fail to provide a record length (LEN value) when you open a random-access file it defaults to 128 bytes. This includes the 2 byte length-prefix added if you Put a String value as the record contents.


    If you really want to do this stuff I suggest you read the VB6 manuals. There are general articles on file I/O as well as specific information under the Open, Get, and Put statements that will be useful.

    However this stuff is really legacy baggage in VB6, only there to help convert old MS-DOS Basic code to VB. Random I/O is deprecated, and the old MS-DOS ISAM isn't even available. VB was meant to use Jet databases for these kinds of things. It doesn't hurt to learn about the old stuff, but very few serious programs ever use it any more and haven't for a very long time. It is just too limiting and it isn't the 1980s anymore.

  3. #3

    Thread Starter
    Lively Member ahmedcrow's Avatar
    Join Date
    Jul 2017
    Posts
    82

    Re: "string * x" with random access files

    Quote Originally Posted by dilettante View Post
    VB's file numbers are not "handles" and calling them that leads to confusion, errors, and frustration.
    Rick Meyer in this web page said "Second - provide a file handle variable."
    http://trixar.com/vbmakai/minidb1.htm#what

    Quote Originally Posted by dilettante View Post
    Naming every variable "varXXX" is sort of silly. What next, name every function "funcXXX" and every subroutine "subXXX" and so on?
    This way isn't standard one, it's a personal one to help me to distinguish diffrent identifiers, it's temporal, I'm still learning and need things help me.

    Quote Originally Posted by dilettante View Post
    If you fail to provide a record length (LEN value) when you open a random-access file it defaults to 128 bytes. This includes the 2 byte length-prefix added if you Put a String value as the record contents.
    In a previouse thread one of members told me that the default record size of randome files is 128, I used it, but what if I need to use user define types and I've in it for example "string * 30" field, it doesn't work, for that I'm asking experinced VB6 programmers.

    Quote Originally Posted by dilettante View Post
    If you really want to do this stuff I suggest you read the VB6 manuals. There are general articles on file I/O as well as specific information under the Open, Get, and Put statements that will be useful.

    However this stuff is really legacy baggage in VB6, only there to help convert old MS-DOS Basic code to VB. Random I/O is deprecated, and the old MS-DOS ISAM isn't even available. VB was meant to use Jet databases for these kinds of things. It doesn't hurt to learn about the old stuff, but very few serious programs ever use it any more and haven't for a very long time. It is just too limiting and it isn't the 1980s anymore.
    Good, but I want to use random access files in small tasks those don't need high abialities of real data bases files.

  4. #4
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    4,583

    Re: "string * x" with random access files

    I mentioned that before in one of your other threads.
    If you don't specify a length for the "record" size of the Random file, it defaults to 128.
    A dynamic string is proceeded by two bytes that specify its length when written to the file.

    So, as long as your dynamic strings are equal to or less than 126 characters, they will fit into the 128 bytes space reserved for each record in the file.
    You don't have to just write strings. You can write any data into any "record" as long as the length doesn't exceed the space reserved.

    When I used random files to store information a long time ago (nearly 30 years ago, before Visual Basic), I didn't write my UDT in the first "record" of the file. I would write information like how many records followed (how many were actually used) in the file in the first record. I would keep the records packed, so when I removed records, there would be unused "records" at the end of the file, so keeping track of how many were valid in the first "record" was a convenience.

    In your case, you write the word "Hello" in the first "record", so the first two bytes in that "record" would be 5 , 0 indicating that a five character string follow, so the next five bytes are the ASCII characters of the string "Hello". After those seven bytes, there should be 121 bytes of zeros to fill out the 128 bytes for that "record".

    The second string follows the same pattern, but is written at byte 129 of the file (after the first 128 byte "record").
    So, at byte 129, you have the 6, 0 bytes, followed by the six characters for "World!"
    In this case, assuming you're writing into a newly created file, so the second "record" is the last record written to, the file will end at the "!" character, i.e. the last byte of the file will contain the ASCII character "!".
    VB doesn't bother padding out the extra 120 bytes at the end of the file needed to fill the 128 byte "record". It will only do that when you write another record beyond the second, in order to pad out to the offset where the record you've written is to be written.
    For instance, assume you wrote the "Hello" to the first record and "World!" to the second record. You then do this.
    Put varFileHandle, 10, "Bye!"

    The String "Bye!" (with its size of course), will be written at byte 1280 of the file, the file will "instantly" be about 10 times larger, with space reserved for records 3 through 9 which you haven't written to. But the last record, number 10, will only be six bytes in size, as it is the last record so isn't padded out to fill unused space after the last record.

    Now the data doesn't actually indicate that those "records" contain strings. It is just the way a dynamic string is written to the file.
    When you read that data back in later, if you read the "record" into a dynamic string variable, then it will "unpack" or "interpret" the bytes correctly, i.e. reading the size of the string from the first two bytes, allocating the space, and reading the number of characters specified follow into that string.

    But, if you said to read record 1 into a Long, then it would read the first 4 bytes of the record into a Long and you would get a fairly large number, instead of the string "Hello".
    Or if you read the first record into a Single, or a Double, you would get other numbers, assuming the 4 bytes or 8 bytes read from the record were a valid combination of bytes for a single or double representation of a number.

    So, it is up to you to know what type of data you've written to the "record", so you read the data back into the same type to get a correct result. And you ensure that you don't clobber data by overwriting the size of record you specified, and that you maintain alignment by using the same record size you used when creating the file.

  5. #5
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    4,583

    Re: "string * x" with random access files

    Quote Originally Posted by ahmedcrow View Post
    ..., but what if I need to use user define types and I've in it for example "string * 30" field, it doesn't work, for that I'm asking experinced VB6 programmers....
    It should work fine, as long as your strings are fixed, and your other data is in the UDT is fixed, then the UDT should have a fixed size and you use the size of the UDT for the record size of the Random file.
    If it isn't working, then you've made a mistake somewhere in specifying the size of the record, most likely. Fixed strings should pad out or be truncated when dynamic strings are assigned to them.

  6. #6
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    4,583

    Re: "string * x" with random access files

    I just pulled an UDT from some old code which processed information from the old Version1 mp3 tag information format, which used to be at the end of the early formatted files. Newer files are more sophisticated, so I don't know what percentage of files currently support this, but that is beside the point.

    For this example, in one button, I'll just use that UDT, fill in some data, and write 10 instances of it to a Random file.
    Then, I'll read back every other record from the file in another button.
    I did this originally, not using the "len =" clause, so each record was aligned to 128 byte boundries, and worked fine as the record size is 128 bytes exactly (probably by design), so fit in the default 128 byte record.
    But, increase the size of any one of these "fields" and it would have caused an exception as you tried to write more than 128 bytes into a 128 byte "bucket".

    The code, as posted, does use the "len =" clause, so if you increased the size of a field, it should still work as it will write and read on a larger byte boundary.
    Code:
    Private Type TagInfo
      Tag As String * 3
      Songname As String * 30
      artist As String * 30
      album As String * 30
      year As String * 4
      comment As String * 30
      Genre As String * 1
    End Type
    
    Private Sub Command1_Click()
      Dim udt As TagInfo
      Open "C:\c\udtTest.dat" For Random As #1 Len = Len(udt)
      
      Dim i As Integer
      With udt
        For i = 1 To 10
          .Tag = Str(i)
          .Songname = "Some song #" & CStr(i)
          .artist = "Chubby #" & CStr(i)
          .album = "Some album that the Chubbys did"  'should get truncated, more than 30 characters
          .year = "1966"
          .comment = "Any old comment #" & CStr(i * 10)
          .Genre = "Fred"   'Should be truncated to "F" I believe.
          
          Put #1, i, udt
        Next
        Close #1
      End With
    End Sub
    
    Private Sub Command2_Click()
      Dim udt As TagInfo
      Open "C:\c\udtTest.dat" For Random As #1 Len = Len(udt)
      
      Dim i As Integer
      With udt
        For i = 1 To 5
          Get #1, i * 2, udt
          
          Debug.Print .Tag, .Songname, .artist
          Debug.Print .album, .year, .comment
          Debug.Print .Genre
          Debug.Print
          
        Next
        Close #1
      End With
    
    End Sub
    The output in the Immediate window.
    Code:
     2            Some song #2                              Chubby #2                     
    Some album that the Chubbys di            1966          Any old comment #20           
    F
    
     4            Some song #4                              Chubby #4                     
    Some album that the Chubbys di            1966          Any old comment #40           
    F
    
     6            Some song #6                              Chubby #6                     
    Some album that the Chubbys di            1966          Any old comment #60           
    F
    
     8            Some song #8                              Chubby #8                     
    Some album that the Chubbys di            1966          Any old comment #80           
    F
    
     10           Some song #10                             Chubby #10                    
    Some album that the Chubbys di            1966          Any old comment #100          
    F

  7. #7

    Thread Starter
    Lively Member ahmedcrow's Avatar
    Join Date
    Jul 2017
    Posts
    82

    Re: "string * x" with random access files

    Quote Originally Posted by passel View Post
    It should work fine, as long as your strings are fixed, and your other data is in the UDT is fixed, then the UDT should have a fixed size and you use the size of the UDT for the record size of the Random file.
    If it isn't working, then you've made a mistake somewhere in specifying the size of the record, most likely. Fixed strings should pad out or be truncated when dynamic strings are assigned to them.
    Great my friend passel, your words gave me a hope, I tried a small change like you said and this's the new code and I've a problem, you can see the message box in the image below, this's the result I have, I don't know the reason, and the program is so small, I read all the code and didn't distinguish the problem, everything is according to the rules of the language.

    Code:
    Dim varFileHandle As Integer
    Dim varString As String * 30
    
    Private Sub Command1_Click()
    Close
    End
    End Sub
    
    Private Sub Command2_Click()
    
    Let varFileHandle = FreeFile
    Let varRecSize = Len(varString)
    
    
    Open App.Path & "\data.dat" For Random As varFileHandle Len = Len(varString)
    
    Get varFileHandle, 2, varString
    
    Close varFileHandle
    
    MsgBox varString
    
    End Sub
    
    Private Sub Form_Load()
    
    Let varFileHandle = FreeFile
    Let varRecSize = Len(varString)
    
    If Not Dir(App.Path & "\data.dat") = "" Then
    Kill (App.Path & "\data.dat")
    End If
    
    Open App.Path & "\data.dat" For Random As varFileHandle Len = Len(varString)
    
    Put varFileHandle, , "Hello"
    Put varFileHandle, , "World!"
    
    Close varFileHandle
    
    End Sub

  8. #8
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    4,583

    Re: "string * x" with random access files

    I expect the problem there is you're writing dynamic literal strings to the file, and trying to read a fixed string, so you're not writing and reading the same type.
    The solution (educated guess, didn't try it) is to set a fixed length string to the string you want to write, and write it, as in the snippet of code below.
    Code:
    Private Sub Form_Load()
    
    '...
    
      Open App.Path & "\data.dat" For Random As varFileHandle Len = Len(varString)
     
        varString = "Hello": Put varFileHandle, , varString
        varString = "World!": Put varFileHandle, , varString
    
      Close varFileHandle
    
    End Sub
    When writing a dynamic string to the file, the first two bytes (for strings less than 64K in size) is the length of the string. (It might be 32K, I'm not sure. There has to be a flag of some sort when the string needs more than 15-bits for the number of characters, so there is some formatting rule for larger strings that I don't know off the top of my head any longer).

    When writing a fixed string to a file, the string starts at the first byte, the size isn't written because it is a fixed length. You are expected to read back into a fixed string of the same length.

    I didn't test it, but since you're trying to read 30 characters back from the file, and you only wrote 8 bytes (2 bytes for the size, and 6 bytes for the string), at best you'll get garbage characters at the front, but most likely you get an access error. If I had a little more time, I would try you code quick to see what it does. Of course, you could have just told us, as well.
    Last edited by passel; Sep 13th, 2018 at 07:07 PM.

  9. #9

    Thread Starter
    Lively Member ahmedcrow's Avatar
    Join Date
    Jul 2017
    Posts
    82

    Re: "string * x" with random access files

    Quote Originally Posted by passel View Post
    I expect the problem there is you're writing dynamic literal strings to the file, and trying to read a fixed string, so you're not writing and reading the same type.
    The solution (educated guess, didn't try it) is to set a fixed length string to the string you want to write, and write it, as in the snippet of code below.
    Code:
    Private Sub Form_Load()
    
    '...
    
      Open App.Path & "\data.dat" For Random As varFileHandle Len = Len(varString)
     
        varString = "Hello": Put varFileHandle, , varString
        varString = "World!": Put varFileHandle, , varString
    
      Close varFileHandle
    
    End Sub
    When writing a dynamic string to the file, the first two bytes (for strings less than 64K in size) is the length of the string. (It might be 32K, I'm not sure. There has to be a flag of some sort when the string needs more than 15-bits for the number of characters, so there is some formatting rule for larger strings that I don't know off the top of my head any longer).

    When writing a fixed string to a file, the string starts at the first byte, the size isn't written because it is a fixed length. You are expected to read back into a fixed string of the same length.

    I didn't test it, but since you're trying to read 30 characters back from the file, and you only wrote 8 bytes (2 bytes for the size, and 6 bytes for the string), at best you'll get garbage characters at the front, but most likely you get an access error. If I had a little more time, I would try you code quick to see what it does. Of course, you could have just told us, as well.
    Dear passel, you're 100% right, it works now.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Featured


Click Here to Expand Forum to Full Width