none
Can you iterate through a structure variable?

    Question

  • I want to save each of the 15 variables in a structure to a file. Is there a way to iterate through the structure to save them to an array so I can then use File.WriteAllLines to save them to a file? Or, is there a way to just save (and then restore) the entire structure as one entity to a file?

    Robert Homes

    Thursday, December 07, 2017 12:06 AM

Answers

  • Acamar,

    It's very nice of you to try to help me!

    My structure contains a number of boolean variables, one integer, and one font! Its the font that makes things difficult.

    I should mention before going much further than I don't see much use (for my situation at least) in including the method to write the structure variable output method inside the structure. I could just as well write it in a separate procedure (if I'm not looking for "portability", which I'm not). In either case, it seems I still have to write out a separate line for each variable, rather than iterating through them. I went ahead and did that, and succeeded in writing AND reading the variables in and out of a file. I didn't use the WriteAllLines thing, instead I used AppendAllText for each line I add to the file (after deleting the old file and creating a new one). But the thing stumbles on the font variable.

    I found I could write the font to the file as a string, where it turns into this:

    [Font: Name=Tempus Sans ITC, Size=12, Units=3, GdiCharSet=0, GdiVerticalFont=False]

    The fontstyle was bold, but I dont see that writing it to the file as a string saved the FontStyle, unless "Units" somehow translates to FontStyle. Anyway, I CAN at least write that much out to the file. But when I read it back in, (and then parse out the Font Name, Font Size and Font "Units", I can't get the FontStyle. I tried this:

       .OptionsFont = New Font(FontName, CDbl(FontSize), CDbl(FontUnits))

    But VB won't compile that, says I didn't include the correct arguments (even though one of the "overloads" for the New Font thing includes using the FontName (ie Family), FontSize, and Units (whatever that is).

    Anyway, I'm just about there except for that Font Style. Thank you very much for your help. Actually, I think I know enough to "get by", but I don't use classes or enumerators (whatever those are) and some of the more intricate stuff. And I actually don't want to learn about all that. I'm just trying to finish a few programs, hopefully the "easy" way, and don't really have enough time to stop and learn a lot of new stuff unless I absolutely have to. My wife is already annoyed about me spending too much time on the computer as it is!!


    Robert Homes



    Robert,

    I'm not necessarily endorsing the following, but I'm trying to comply with what I think you might have (or close to it).

    Acamar is right in that serialization is very easy - once you "get it" as for what's going on. We can talk about all of that later but you might try the following in a new test project just to see that it does in fact work.

    I start with a structure and a class. The class has two shared methods (I'll show how to use them shortly):

    <Serializable()> _
    Public Structure MyData
        Public Property B1 As Boolean
        Public Property B2 As Boolean
        Public Property B3 As Boolean
        Public Property I1 As Integer
        Public Property I2 As Integer
        Public Property I3 As Integer
        Public Property F1 As Font
        Public Property F2 As Font
        Public Property F3 As Font
    End Structure
    
    
    
    
    
    Public Class SharedMethods
        Private Sub New()
        End Sub
    
        ''' <summary>
        ''' A method to serialize an object and save it to a binary file.
        ''' </summary>
        ''' <param name="data">The data to serialize.</param>
        ''' <param name="filePath">The full filepath where this binary file is to be saved to.</param>
        ''' <remarks></remarks>
        Public Shared Sub SaveBinaryData(ByVal data As Object, _
                                         ByVal filePath As String)
    
            Try
                Using fs As New IO.FileStream(filePath, IO.FileMode.Create)
                    Dim formatter As New Runtime.Serialization.Formatters.Binary.BinaryFormatter
                    formatter.Serialize(fs, data)
                End Using
    
            Catch ex As Exception
                Throw
            End Try
    
        End Sub
    
        ''' <summary>
        ''' A method to load a binary file which was serialized using the 
        ''' "SaveBinaryData" method into an object which is returned.
        ''' </summary>
        ''' <param name="filePath">The full filepath where this binary file is to be loaded from.</param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Shared Function LoadBinaryData(ByVal filePath As String) _
            As Object
    
            Dim retVal As Object = Nothing
    
            Try
                Using fs As New IO.FileStream(filePath, IO.FileMode.Open)
                    Dim formatter As New Runtime.Serialization.Formatters.Binary.BinaryFormatter()
                    retVal = formatter.Deserialize(fs)
                End Using
    
            Catch ex As Exception
                retVal = Nothing
                Throw
            End Try
    
            Return retVal
    
        End Function
    End Class

    Please do note that the structure has to be tagged with the serializable attribute (like you see above) or it won't work.

    Now I'll create a single instance of that structure then save it to a file that I'll put on my desk"prettyprint lang-vb">Public Class Form1 Private Sub _ Form1_Load(sender As Object, _ e As EventArgs) _ Handles MyBase.Load Dim data As New MyData _ With {.B1 = True, .B2 = False, .B3 = False, _ .I1 = 1, .I2 = 2, .I3 = 3, _ .F1 = New Font("Tahoma", 9, FontStyle.Bold), _ .F3 = New Font("Calibri", 12, FontStyle.Italic)} Dim desktop As String = _ Environment.GetFolderPath(Environment.SpecialFolder.Desktop) Dim filePath As String = IO.Path.Combine(desktop, "TestMe.bin") SharedMethods.SaveBinaryData(data, filePath) Dim newData As MyData = _ DirectCast(SharedMethods.LoadBinaryData(filePath), MyData) Stop End Sub End Class

    
    

    
    

    Stepping through that, "data" is a new instance of the structure. You can see that I've initialized some of the data (all of them except one) which includes what you say above: Booleans, integers, and fonts.

    I then save it to a file on my desktop and if you look at the file using an ordinary text file editor, you'll see this:

    Binary serialization retains the identity and you can see above how it's doing that. You also "see" the data itself (even though it's not all readable).

    Following that, to prove that it worked and that I can get the data back from the file, I create another instance of the structure ("newData") and using the file as the basis, I then cast it to the structure. To make sure it works, have a look at it:

    Is this somewhere close to what you want to do? If you have a collection of them (a list or other collection) then that can be dealt with also - but it's not included here.

    ***** EDIT *****

    I don't know what happened to the formatting but I didn't paste anything in. At any rate though, I did leave out something important: Check whether or not the file exists and if it does, DELETE it first or the filestream will throw an exception.


    "A problem well stated is a problem half solved.” - Charles F. Kettering



    • Edited by Frank L. Smith Thursday, December 07, 2017 2:15 PM
    • Marked as answer by Robert Homes Thursday, December 07, 2017 3:16 PM
    Thursday, December 07, 2017 2:06 PM

All replies

  • I want to save each of the 15 variables in a structure to a file. Is there a way to iterate through the structure to save them to an array so I can then use File.WriteAllLines to save them to a file? Or, is there a way to just save (and then restore) the entire structure as one entity to a file?

    Robert Homes

    Hi Robert,

    Do you mean that you want to save the value of the properties of a single instance of a structure?

    If so there are many ways - CSV and XML come to mind. If that's not what you mean then explain more please?


    "A problem well stated is a problem half solved.” - Charles F. Kettering

    Thursday, December 07, 2017 12:08 AM
  • Yes, I think that's what I mean. I just want to save all the values in a single instance of a structure. How do you to that with iteration using CSV (which I assume means "comma separated values")?

    Robert Homes


    • Edited by Robert Homes Thursday, December 07, 2017 12:19 AM
    Thursday, December 07, 2017 12:18 AM
  • I want to save each of the 15 variables in a structure to a file. Is there a way to iterate through the structure to save them to an array so I can then use File.WriteAllLines to save them to a file? Or, is there a way to just save (and then restore) the entire structure as one entity to a file?

    Structures support methods and properties in the same way that classes do.  You could implement an ienumerable interface to allow For/Each looping through the variables. Or just add a function that returns a collection (eg, a List) of the variables. Those processes would be suitable for a structured file storage method, such as XML.

    If the structure (or, at least, those variables that you need to store) is serializable then you can serialize the whole structure to a file.

    Thursday, December 07, 2017 12:19 AM
  • Acamar, thanks for trying! But I'm unfamiliar with these concepts. How would I "add a function that returns a collection of the variables"? Don't know what "serializable" means. I think I know what you mean by "methods and properties" in a structure, but don't know how to start creating a method to save the structured variables in a list by iterating though the variables in the structure.

    Robert Homes

    Thursday, December 07, 2017 12:25 AM
  • Yes, I think that's what I mean. I just want to save all the values in a single instance of a structure. How do you to that with iteration using CSV (which I assume means "comma separated values")?

    Robert Homes


    With no more time given than I have, this is a start.

    Let's start with using a class rather than a structure though:

    Public Class MyNewClass
        Private _name As String
        Private _age As Integer
    
        Public Sub New(ByVal name As String, _
                       ByVal age As Integer)
    
            If Not String.IsNullOrWhiteSpace(name) Then
                If age > 0 Then
                    _name = name.Trim
                    _age = age
                End If
            End If
    
        End Sub
    
        Public Sub ExportToCSV(ByVal filePath As String)
    
            ' Read the values of _name and _age
            ' and write the values to a stringbuilder.
            ' 
            ' Note that for the age, you'll want to 
            ' write the value.ToString.
    
        End Sub
    
        Public Sub ImportFromCSV(ByVal filePath As String)
    
            ' Essentially do the opposite here.
            '
            ' If this is a class library, create
            ' a FRIEND parameterless constructor
            ' to make life easier.
    
        End Sub
    
        Public ReadOnly Property Age As Integer
            Get
                Return _age
            End Get
        End Property
    
        Public ReadOnly Property Name As String
            Get
                Return _name
            End Get
        End Property
    End Class
    
    

    I left the big part out but it's not hard; neither is XML.

    I feel sure you'll have it answered by time I get back tomorrow mid-morning but if you want, we can discuss the specifics then and I'll step you through each part of it (including XML if you want).


    "A problem well stated is a problem half solved.” - Charles F. Kettering

    Thursday, December 07, 2017 12:26 AM
  • I'm unfamiliar with these concepts. How would I "add a function that returns a collection of the variables"? Don't know what "serializable" means.

    If you have a structure like this:

    Public Structure myStructure
        Public A1 As Integer
        Public A2 As Integer
        Public A3 As Integer
    
        Public ReadOnly Property AList As List(Of Integer)
            Get
                Dim thisList As New List(Of Integer)
                thisList.Add(A1)
                thisList.Add(A2)
                thisList.Add(A3)
                Return thisList
            End Get
        End Property
    End Structure

    Then you could write code like this:

    Dim thisStruct As New myStructure With {.a1 = 1, .a2 = 2, .a3 = 3}

    ...

    For Each I As Integer In thisStruct.AList ' Do something with I Next

    If you particularly want to use WrteAllLines then the property would return a series of lines as one string.  What are the Types of these variables?

    Serialization is by far the simplest method, but the variables need to be a type that is serializable.  It's just a procedure for converting a series of variables (specifically, those variables within one class or structure that are marked as serializable) into a single binary stream, and writing that stream out to a file.  Then the file can be read back in as a single binary stream and reconstructed into a copy of the original class (or structure). See:
    https://docs.microsoft.com/en-us/dotnet/visual-basic/programming-guide/concepts/serialization/

    Thursday, December 07, 2017 12:42 AM
  • Acamar,

    It's very nice of you to try to help me!

    My structure contains a number of boolean variables, one integer, and one font! Its the font that makes things difficult.

    I should mention before going much further than I don't see much use (for my situation at least) in including the method to write the structure variable output method inside the structure. I could just as well write it in a separate procedure (if I'm not looking for "portability", which I'm not). In either case, it seems I still have to write out a separate line for each variable, rather than iterating through them. I went ahead and did that, and succeeded in writing AND reading the variables in and out of a file. I didn't use the WriteAllLines thing, instead I used AppendAllText for each line I add to the file (after deleting the old file and creating a new one). But the thing stumbles on the font variable.

    I found I could write the font to the file as a string, where it turns into this:

    [Font: Name=Tempus Sans ITC, Size=12, Units=3, GdiCharSet=0, GdiVerticalFont=False]

    The fontstyle was bold, but I dont see that writing it to the file as a string saved the FontStyle, unless "Units" somehow translates to FontStyle. Anyway, I CAN at least write that much out to the file. But when I read it back in, (and then parse out the Font Name, Font Size and Font "Units", I can't get the FontStyle. I tried this:

       .OptionsFont = New Font(FontName, CDbl(FontSize), CDbl(FontUnits))

    But VB won't compile that, says I didn't include the correct arguments (even though one of the "overloads" for the New Font thing includes using the FontName (ie Family), FontSize, and Units (whatever that is).

    Anyway, I'm just about there except for that Font Style. Thank you very much for your help. Actually, I think I know enough to "get by", but I don't use classes or enumerators (whatever those are) and some of the more intricate stuff. And I actually don't want to learn about all that. I'm just trying to finish a few programs, hopefully the "easy" way, and don't really have enough time to stop and learn a lot of new stuff unless I absolutely have to. My wife is already annoyed about me spending too much time on the computer as it is!!


    Robert Homes


    • Edited by Robert Homes Thursday, December 07, 2017 2:15 AM
    Thursday, December 07, 2017 2:12 AM
  • I should mention before going much further than I don't see much use (for my situation at least) in including the method to write the structure variable output method inside the structure. I could just as well write it in a separate procedure (if I'm not looking for "portability", which I'm not). In either case, it seems I still have to write out a separate line for each variable, rather than iterating through them. I went ahead and did that, and succeeded in writing AND reading the variables in and out of a file.

    Code that references structure variables by name should be part of the structure.  Code in other places won't know what the names are (unless it uses reflection, which is becoming very complex).  There's no point in scattering this code around in different places in your application.

    You haven't shown the code used to create the string representation of the font, but there should be no difficulty in creating one that includes the styles (or just appending those styles to the existing string) using the same comma-separated format.  You would need to iterate over the possible styles and either add an indicator (eg, 0 / 1) for each style according to whether it's set or not, or just append the name of any style that is set.

    Thursday, December 07, 2017 2:43 AM
  • Acamar,

    It's very nice of you to try to help me!

    My structure contains a number of boolean variables, one integer, and one font! Its the font that makes things difficult.

    I should mention before going much further than I don't see much use (for my situation at least) in including the method to write the structure variable output method inside the structure. I could just as well write it in a separate procedure (if I'm not looking for "portability", which I'm not). In either case, it seems I still have to write out a separate line for each variable, rather than iterating through them. I went ahead and did that, and succeeded in writing AND reading the variables in and out of a file. I didn't use the WriteAllLines thing, instead I used AppendAllText for each line I add to the file (after deleting the old file and creating a new one). But the thing stumbles on the font variable.

    I found I could write the font to the file as a string, where it turns into this:

    [Font: Name=Tempus Sans ITC, Size=12, Units=3, GdiCharSet=0, GdiVerticalFont=False]

    The fontstyle was bold, but I dont see that writing it to the file as a string saved the FontStyle, unless "Units" somehow translates to FontStyle. Anyway, I CAN at least write that much out to the file. But when I read it back in, (and then parse out the Font Name, Font Size and Font "Units", I can't get the FontStyle. I tried this:

       .OptionsFont = New Font(FontName, CDbl(FontSize), CDbl(FontUnits))

    But VB won't compile that, says I didn't include the correct arguments (even though one of the "overloads" for the New Font thing includes using the FontName (ie Family), FontSize, and Units (whatever that is).

    Anyway, I'm just about there except for that Font Style. Thank you very much for your help. Actually, I think I know enough to "get by", but I don't use classes or enumerators (whatever those are) and some of the more intricate stuff. And I actually don't want to learn about all that. I'm just trying to finish a few programs, hopefully the "easy" way, and don't really have enough time to stop and learn a lot of new stuff unless I absolutely have to. My wife is already annoyed about me spending too much time on the computer as it is!!


    Robert Homes



    Robert,

    I'm not necessarily endorsing the following, but I'm trying to comply with what I think you might have (or close to it).

    Acamar is right in that serialization is very easy - once you "get it" as for what's going on. We can talk about all of that later but you might try the following in a new test project just to see that it does in fact work.

    I start with a structure and a class. The class has two shared methods (I'll show how to use them shortly):

    <Serializable()> _
    Public Structure MyData
        Public Property B1 As Boolean
        Public Property B2 As Boolean
        Public Property B3 As Boolean
        Public Property I1 As Integer
        Public Property I2 As Integer
        Public Property I3 As Integer
        Public Property F1 As Font
        Public Property F2 As Font
        Public Property F3 As Font
    End Structure
    
    
    
    
    
    Public Class SharedMethods
        Private Sub New()
        End Sub
    
        ''' <summary>
        ''' A method to serialize an object and save it to a binary file.
        ''' </summary>
        ''' <param name="data">The data to serialize.</param>
        ''' <param name="filePath">The full filepath where this binary file is to be saved to.</param>
        ''' <remarks></remarks>
        Public Shared Sub SaveBinaryData(ByVal data As Object, _
                                         ByVal filePath As String)
    
            Try
                Using fs As New IO.FileStream(filePath, IO.FileMode.Create)
                    Dim formatter As New Runtime.Serialization.Formatters.Binary.BinaryFormatter
                    formatter.Serialize(fs, data)
                End Using
    
            Catch ex As Exception
                Throw
            End Try
    
        End Sub
    
        ''' <summary>
        ''' A method to load a binary file which was serialized using the 
        ''' "SaveBinaryData" method into an object which is returned.
        ''' </summary>
        ''' <param name="filePath">The full filepath where this binary file is to be loaded from.</param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Shared Function LoadBinaryData(ByVal filePath As String) _
            As Object
    
            Dim retVal As Object = Nothing
    
            Try
                Using fs As New IO.FileStream(filePath, IO.FileMode.Open)
                    Dim formatter As New Runtime.Serialization.Formatters.Binary.BinaryFormatter()
                    retVal = formatter.Deserialize(fs)
                End Using
    
            Catch ex As Exception
                retVal = Nothing
                Throw
            End Try
    
            Return retVal
    
        End Function
    End Class

    Please do note that the structure has to be tagged with the serializable attribute (like you see above) or it won't work.

    Now I'll create a single instance of that structure then save it to a file that I'll put on my desk"prettyprint lang-vb">Public Class Form1 Private Sub _ Form1_Load(sender As Object, _ e As EventArgs) _ Handles MyBase.Load Dim data As New MyData _ With {.B1 = True, .B2 = False, .B3 = False, _ .I1 = 1, .I2 = 2, .I3 = 3, _ .F1 = New Font("Tahoma", 9, FontStyle.Bold), _ .F3 = New Font("Calibri", 12, FontStyle.Italic)} Dim desktop As String = _ Environment.GetFolderPath(Environment.SpecialFolder.Desktop) Dim filePath As String = IO.Path.Combine(desktop, "TestMe.bin") SharedMethods.SaveBinaryData(data, filePath) Dim newData As MyData = _ DirectCast(SharedMethods.LoadBinaryData(filePath), MyData) Stop End Sub End Class

    
    

    
    

    Stepping through that, "data" is a new instance of the structure. You can see that I've initialized some of the data (all of them except one) which includes what you say above: Booleans, integers, and fonts.

    I then save it to a file on my desktop and if you look at the file using an ordinary text file editor, you'll see this:

    Binary serialization retains the identity and you can see above how it's doing that. You also "see" the data itself (even though it's not all readable).

    Following that, to prove that it worked and that I can get the data back from the file, I create another instance of the structure ("newData") and using the file as the basis, I then cast it to the structure. To make sure it works, have a look at it:

    Is this somewhere close to what you want to do? If you have a collection of them (a list or other collection) then that can be dealt with also - but it's not included here.

    ***** EDIT *****

    I don't know what happened to the formatting but I didn't paste anything in. At any rate though, I did leave out something important: Check whether or not the file exists and if it does, DELETE it first or the filestream will throw an exception.


    "A problem well stated is a problem half solved.” - Charles F. Kettering



    • Edited by Frank L. Smith Thursday, December 07, 2017 2:15 PM
    • Marked as answer by Robert Homes Thursday, December 07, 2017 3:16 PM
    Thursday, December 07, 2017 2:06 PM
  • Public Class Form1
        Private Sub _
            Form1_Load(sender As Object, _
                       e As EventArgs) _
                       Handles MyBase.Load
    
            Dim data As New MyData _
                With {.B1 = True, .B2 = False, .B3 = False, _
                      .I1 = 1, .I2 = 2, .I3 = 3, _
                      .F1 = New Font("Tahoma", 9, FontStyle.Bold), _
                      .F3 = New Font("Calibri", 12, FontStyle.Italic)}
    
            Dim desktop As String = _
                Environment.GetFolderPath(Environment.SpecialFolder.Desktop)
    
            Dim filePath As String = IO.Path.Combine(desktop, "TestMe.bin")
    
            SharedMethods.SaveBinaryData(data, filePath)
    
            Dim newData As MyData = _
                DirectCast(SharedMethods.LoadBinaryData(filePath), MyData)
    
            Stop
    
        End Sub
    End Class

    This forum is screwy again...


    "A problem well stated is a problem half solved.” - Charles F. Kettering

    Thursday, December 07, 2017 2:16 PM
  • It worked! Frank, you're great! Here's a quote for you:

    Any sufficiently advanced technology is indistinguishable from magic.

         - Arthur C. Clarke

    That's what your code is to me -- magic!

    Thanks.


    Robert Homes


    • Edited by Robert Homes Thursday, December 07, 2017 3:16 PM
    Thursday, December 07, 2017 3:15 PM
  • Acamar,

    I do see your point about including methods that handle the structure within the structure itself. I'll remember that and do it from now on.


    Robert Homes

    Thursday, December 07, 2017 3:18 PM
  • It worked! Frank, you're great! Here's a quote for you:

    Any sufficiently advanced technology is indistinguishable from magic.

         - Arthur C. Clarke

    That's what your code is to me -- magic!

    Thanks.


    Robert Homes


    :)

    If you're up for it, let's talk some about what's going on with that. Also, as you could see in the screenshot, the data isn't hidden at all.

    If you need that I'll show you another way to get that. It gets involved but if you need the "security" of it, it can be done.


    "A problem well stated is a problem half solved.” - Charles F. Kettering

    Thursday, December 07, 2017 3:20 PM
  • Robert,

    Why do you want to use so badly a structure. Did you buy a new computer which has to much memory and you want to see how to get it completely full?


    Success
    Cor

    Thursday, December 07, 2017 3:20 PM
  • Cor, ha ha

    Structure is what I know. I don't know what you know.

    As far as using memory, I'm not creating an array of the structure, just one instance. So I don't think that will get my computer "completely full".


    Robert Homes



    • Edited by Robert Homes Thursday, December 07, 2017 6:08 PM
    Thursday, December 07, 2017 6:04 PM