Fixed width string to sequential structure - vb.net

I'm new to VB.NET so I'm not sure if this a dumb question or not..
So I have a large fixed width string I want to parse out into a fixed width structure. I wanted to know if I could just assign it to a sequential structure (Maybe using pointers?) or if using TextFieldParser and individually assigning each field was the only way to go. I have a large string that represents probably 100 different fields but for example purposes of what I'm trying to accomplish I'll provide this.
I'm calling a function that will return a fixed width record like so lets say I have a record like this...
Dim inRec as String("FIRST NAME LAST NAME 03302016")
I looked at the MSDN article about creating fixed structure layouts and I don't think I understood it well...
https://msdn.microsoft.com/en-us/library/s9ts558h(v=vs.110).aspx
From their example could I create a structure like this...
<StructLayout(LayoutKind.Sequential, CharSet := CharSet.Unicode)> _
Structure My_Record
<MarshalAs(UnmanagedType.ByValTStr, SizeConst := 21)> Public fName As String
<MarshalAs(UnmanagedType.ByValTStr, SizeConst := 13)> Public lName As String
<MarshalAs(UnmanagedType.ByValTStr, SizeConst := 8)> Public mDate As String
End Structure
Dim currentRecord as New My_Record
Is there a way to assign the inRec string to currentRecord so that the string will line up to fixed width fields in the structure? Or would breaking down inRec using FieldWidths in TextFieldParser then individually assigning them to members of the struct be the only way to go about breaking down a fixed width record like this?

Related

How to Save/Reload data in vb.net after .exe close?

I am new to vb.net, and this is my first project where I'm fairly certain there is an obvious answer that I just can't find.
Problem: I have a list of a structure I have defined with many properties. I want to be able to edit and load that list with the values I have saved to it before hand after closing the program and loading it backup. What is the best way to do this?
This isn't a simple string or bool, otherwise I would use the user settings that is commonly suggested, in the project's properties. I've seen others that save it into an xml and take it back up, but I'm not inclined to do so since this is going to be distributed to others in mass. Since it's a complex structure, what's the commonly held preferred method?
Example
Here's a structure:
Structure animal
Dim coloring as string
Dim vaccinesUpToDate as Boolean
Dim species as string
Dim age as integer
End structure
And there's a List(Of animal) that the user will add say 1 cat, 2 dogs, etc. I want it so that once the programs is closed after the user has added these, that structure will be saved to still have that 1 cat and 2 dogs with those settings so I can display them again. What's the best way to save the data in my program?
Thanks!
Consider serialization. For this, a class is more in order than an old fashioned Struct:
<Serializable>
Class Animal
Public Property Name As String
Public Property Coloring As String
Public Property VaccinesUpToDate As Boolean
Public Property Species As String
Public Property DateOfBirth As DateTime
Public ReadOnly Property Age As Integer
Get
If DateOfBirth <> DateTime.MinValue Then
Return (DateTime.Now.Year - DateOfBirth.Year)
Else
Return 0 ' unknown
End If
End Get
End Property
' many serializers require a simple CTor
Public Sub New()
End Sub
Public Overrides Function ToString() As String
Return String.Format("{0} ({1}, {2})", Name, Species, Age)
End Function
End Class
The ToString() override can be important. It is what will display if you add Animal objects to a ListBox e.g.: "Stripe (Gremlin, 27)"
Friend animalList As New List(of Animal) ' a place to store animals
' create an animal
a = New Animal
a.Coloring = "Orange"
a.Species = "Feline" ' should be an Enum maybe
a.Name = "Ziggy"
a.BirthDate = #2/11/2010#
animalList.Add(a)
' animalList(0) is now the Ziggy record. add as many as you like.
In more complex apps, you might write an Animals collection class. In that case, the List might be internal and the collection could save/load the list.
Friend Sub SaveData(fileName as String)
Using fs As New System.IO.FileStream(fileName,
IO.FileMode.OpenOrCreate)
Dim bf As New BinaryFormatter
bf.Serialize(fs, animalList)
End Using
End Sub
Friend Function LoadData(fileName as String) As List(Of Animal)
Dim a As List(of Animal)
Using fs As New FileStream(fileName, FileMode.Open, FileAccess.Read)
Dim bf As New BinaryFormatter
a = CType(bf.Deserialize(fs), List(Of Animal))
End Using
Return a
End Function
XMLSerialization, ProtoBuf and even json are much the same syntax. For a small amount of data, a serialized list is an easy alternative to a database (and have many, many other uses, like a better Settings approach).
Calculated Fields as Properties
Notice that I added a BirthDate property and changed Age to calculate the result. You should not save anything which can be easily calculated: in order to update the Age (or VaccinesUpToDate) you'd have to 'visit' each record, perform a calculation then save the result - which might be wrong in 24 hours.
The reason for exposing Age as a Property (rather than a function) is for data binding. It is very common to use a List<T> as the DataSource:
animalsDGV.DataSource = myAnimals
The result will be a row for each animal with each Property as a column. Fields as in the original Structure won't show up. Nor would an Age() function display, wrapping the result as a readonly property displays it. In a PropertyGrid, it will show disabled because it is RO.
Class versus Structure
So if a Structure using Properties will work, why use a Class instead? From Choosing Between Class and Struct on MSDN, avoid using a Structure unless the type meets all of the following:
It logically represents a single value, similar to primitive types (int, double, etc.)
It has an instance size under 16 bytes
It is immutable
It will not have to be boxed frequently
Animal fails the first 3 points (while it is a local item it is not a value for #1). It may also fail the last depending on how it is used.

How to store Image Data in SQL VARCHAR(MAX) without a SQLCommand object

I've Googled the underlying question of how to store an image in the database and found many examples. However, all of the answers I'm seeing (for example this), seem to use a SQLCommand as the solution whether they are using the older "image" type, text, or suggesting VARCHAR(MAX). I am using SQL 2008 and SQL 2012
I'm having trouble incorporating this into my app since I have a legacy dll which will do some other work, and then store my image. I want to wrap this into a SQL transaction. The challenge is that the DLL is essentially a black-box to me since I cannot change the source code. The Dll exposes a parameter for an optional SQL statement it can run as part of its transaction.
The structure of the DLL is something like this:
Public Class MyClass
Public Property ExtraSQL as string
Public Function DoWork
Dim sMainSQL as String
'Start A transaction
Execute(sMainSQL)
If ExtraSQL <> "" Then
Execute(ExtraSQL)
End If
If OK Then Commit Transaction
End Function
End Class
My goal is to feed a SQL string into property ExtraSQL, but I'm not sure how to do this as all the examples are indicating something SqlCommand.Parameters(#Image).Value = byteArray(). I'm getting the image data from a web service. Over its life cycle, it's formatted as an Image, a base64 Encoded string, a memorystream and a byte array, so converting between the types is not the problem. I'm just unclear on the cleanest syntax for doing this. Is it best to just take my original base64 string and do something like:
INSERT INTO tblImage (ImageData) Values ('Base64 blah blah')
and then whenever I read the image back out do this:
Public Function Base64ToImage(ByVal base64String As String) As System.Drawing.Image
Dim img As System.Drawing.Image = Nothing
Try
'Convert Base64 string to byte array
Dim btImage As Byte() = Convert.FromBase64String(base64String)
Dim ms As New IO.MemoryStream(btImage, 0, btImage.Length)
ms.Write(btImage, 0, btImage.Length)
img = System.Drawing.Image.FromStream(ms, True)
Catch ex As Exception
End Try
Return img
End Function
I would appreciate any supporting links, examples, and advice. Thank you in advance.
Well, considering you clearly can't send in parameterized SQL, yes that's the best approach. I sure don't like it because it's wide open to SQL injection. I would consider decompiling the assembly you're using and making it my own source code in the future.

vb.net string - replace placeholder tokens efficiently

I'm just looking for some ideas with a little problem. I've a string that's a template for a message, for example:
Dear [[Guest.FirstName]],
We hope your are looking forward to your holiday in [[Booking.ResortName]]
My current system for replacing the spaceholder tokens (e.g. [[Guest.FirstName]]) is very inefficient, there's loops inside loops and it takes for too long.
What I'm looking for is a way that would go through the string until it finds [[something]], then replace that [[something]] with it's real value, then continue on through the string.
Please note to replace [[something]] then I need to have access to the space holder (i.e. i need to know if it's [[Guest.FirstName]] or [[Booking.ResortName]]).
Any suggestions for an efficient way to achieve this would be very much appreciated, I feel it should be fairly straightforward but everything I can think of ends up with loops inside loops again.
Thanks!
Phil.
There are many factors which will affect performance, so it's hard to say, without knowing all the specifics, what method will be most efficient. However, when it comes to coding simplicity, using a RegEx MatchEvaluator would be an excellent option. I'm sure you'll agree that it's much cleaner than whatever you are currently using:
Public Function ReplacePlaceholders(data As String) As String
Dim r As New Regex("\[\[(?<placeholder>.*?)\]\]")
data = r.Replace(data, New MatchEvaluator(AddressOf PlaceHolderReplacementEvaluator))
End Function
Private Function PlaceHolderReplacementEvaluator(match As Match) As String
Dim name As String = match.Groups("placeholder").Value
Return LookUpValueByPlaceholderName(name) ' Replace with value lookup logic here
End Function
If the total number of placeholders in the data is going to be rather small, and the list of possible placeholders is small, it's probably best to just have a list of them with their values and replace them like this:
Public Function ReplacePlaceholders(data As String) As String
Dim placeHolders As Dictionary(Of String, String) = LoadPlaceHolders()
For Each i As KeyValuePair(Of String, String) In placeHolders
data = data.Replace(i.Key, i.Value)
Next
Return data
End Function
Private Function LoadPlaceHolders() As Dictionary(Of String, String)
Dim placeholders As New Dictionary(Of String, String)
' Load data here
Return placeholders
End Function
If you really want the most efficient solution, though, going character by character and appending, as you go, to a StringBuilder or an output Stream, is going to be your best option. It's not going to be pretty, but if you post what you have to CodeReview, there may be some people who could find ways of making it slightly less ugly :)

How can a structure with reserved space be marshalled?

Problem:
I have a struct of a fixed size that I'm trying to marshal. This struct contains a number of useful fields for the current version of the struct and a specified amount of unused space at the end that is reserved for future modifications.
How should I design this structure so that the size of the reserved space will be automatically updated when I modify the structure?
While the following would solve my problem
'Variable size structure
<StructLayout(LayoutKind.Sequential, Pack:=1)>
Structure UsefulData
Dim foo As SByte
Dim bar As Integer
Dim foobar As Short
End Structure
Const MAX_SIZE As Integer = 20
'Fixed size structure
<StructLayout(LayoutKind.Sequential, Pack:=1, Size:=MAX_SIZE>
Structure Data
Dim current As UsefulData
<MarshalAs(UnmanagedType.ByValArray, SizeConst:=MAX_SIZE-System.Runtime.InteropServices.Marshal.SizeOf(GetType(UsefulData)))>
Dim reserved As SByte()
End Structure
but doesn't compile as System.Runtime.InteropServices.Marshal.SizeOf(GetType(UsefulData)) is not a constant expression. Any ideas?
Upon further reflection on this problem I've come to question the validity of my desire to expose the reserved space at the end of the struct. In reality nothing should ever reference the reserved section. If something ever needed to, then the correct approach would be to modify the structure itself to expose the relevant parts of the reserved space.
Consequently the structure should look as follows:
<StructLayout(LayoutKind.Sequential, Size:=20, Pack:=1)>
Structure Data
Dim foo As SByte
Dim bar As Integer
Dim foobar As Short
End Structure
While I don't think hard-coding the array length is the ideal solution, I'm placing this answer here in case there are no other alternatives.
Private const TOTAL_SIZE As Integer = 20
Private const RESERVED_SIZE As Integer = 7
<StructLayout(LayoutKind.Sequential, Pack:=1)>
Structure Data
Dim foo As SByte
Dim bar As Integer
Dim foobar As Short
<MarshalAs(UnManagedType.ByValArray, SizeConst:=RESERVED_SIZE)>
Dim reserved As SByte()
End Structure
I could then add either a unit test or custom build action to ensure that System.Runtime.InteropServices.Marshal.Sizeof(GetType(Data)) = TOTAL_SIZE.

Converting a VB6 module to VB.NET

I'm almost done converting a module from VB6 to VB.NET, but I'm having trouble with the following 2 quotes and am wondering if there's any way to go about this:
Structure AUDINPUTARRAY
bytes(5000) As Byte
End Structure
I'm trying to change that bytes line to: Dim bytes(5000) as Byte
but it's not letting me define the size in a structure.
Here's the second one:
Private i As Integer, j As Integer, msg As String * 200, hWaveIn As integer
I haven't a clue on how to convert: msg As String * 200
you cannot declare an initial size in VB.Net , you can set its size later using Redim statement in constructor or wherever needed
Structure AUDINPUTARRAY
Public bytes() As Byte
Public Sub New(ByVal size As Integer)
ReDim bytes(size) ' set size=5000
End Sub
End Structure
In Visual Basic .NET, you cannot declare a string to have a fixed length unless you use the VBFixedStringAttribute Class attribute in the declaration. The code in the preceding example causes an error.
You declare a string without a length. When your code assigns a value to the string, the length of the value determines the length of the string
see http://msdn.microsoft.com/en-us/library/f47b0zy4%28v=vs.71%29.aspx
. so your declarration will become
Private i As Integer, j As Integer, hWaveIn As Integer
<VBFixedString(200)> Private msg As String
You can do this via attributes
Public Structure <StructLayout(LayoutKind.Sequential)> AUDINPUTARRAY
Public <MarshalAs(UnmanagedType.ByValArray, SizeConst := 5000)>
Bytes() As Byte
End Structure
I would suggest that, while refactoring your code from VB6 to .net, that you take another look at whether you even want to emulate the fixed-length msg As String * 200. If you were counting on the fixed-length string so that you could chop characters off of the end, and still have a 200-character record, that's messy code that depends on a function's side effects.
When we converted from VB6 (a still-ongoing process), it made the intent of the code clearer if we explicitly set the string to a 200-byte block of spaces. Perhaps by declaring:
String msg = String(' ', 200)
(if that's valid in VB.net as well as C#).