How to properly turn this string into a correct structure? - vb.net

Structure WeightElement 'The Weights are stored in an array of Weight Elemenets, which are used as a tree
Dim LowPointer As Integer
Dim HighPointer As Integer
Dim TraitPointer As Integer 'This pointer allows another tree to be appended to certain elements, for the sake of dependent traits
Dim Values As WeightedTrait()
Dim Num As Integer 'The Weight Elements are named by numbers for ease of comparison
End Structure
Structure WeightedTrait
Dim TraitName As String 'This is the value that is displayed to the user when a character is made
Dim TraitNum As Integer 'The trait number is used to find the correct element in trait sub-trees
Dim WeightValue As Decimal
End Structure
Function ChewQuery(QueryList As String)
'This function takes in a string and partitions it into a weight tree
Dim ElementList() As String
ElementList = QueryList.Split(";")
Dim LoadedWeight(ElementList.Length) As WeightElement
For x = 0 To ElementList.Length - 1
'Using this method, in which the front portion of the query is repeatedly removed, allows for a simpler query structure, so that we don't need to partition between the pointers and values
LoadedWeight(x).LowPointer = CInt(BiteQuery(ElementList(x)))
LoadedWeight(x).HighPointer = CInt(BiteQuery(ElementList(x)))
LoadedWeight(x).TraitPointer = CInt(BiteQuery(ElementList(x)))
LoadedWeight(x).Num = CInt(BiteQuery(ElementList(x)))
ChewQueryValues(ElementList(x), LoadedWeight(x))
Next
Return LoadedWeight
End Function
Function BiteQuery(ByRef QueryList As String)
Dim Marker As Integer
Dim Bite As String
'This function partitions the input string around the first comma
'It returns the section before the comma, and stores the section behind the comma as the new value for the string
Try
Marker = InStr(QueryList, ",")
Bite = Left(QueryList, Marker - 1)
Marker = Len(QueryList) - Marker
QueryList = Right(QueryList, Marker)
Catch
'This is used in the case that a list without a comma is input
Bite = QueryList
QueryList = ""
End Try
Return Bite
End Function
Sub ChewQueryValues(ByRef QueryList As String, ByRef LoadedWeight As WeightElement)
LoadedWeight.Values = {}
While Len(QueryList) > 0
'This While loop is so that an arbitrary number of values can be inserted
'Because BiteQuery takes in functions by reference, each loop reduces the length of the string until it is empty
ReDim Preserve LoadedWeight.Values(LoadedWeight.Values.Length + 1)
LoadedWeight.Values(LoadedWeight.Values.Length - 1).TraitName = BiteQuery(QueryList)
LoadedWeight.Values(LoadedWeight.Values.Length - 1).TraitNum = CInt(BiteQuery(QueryList))
LoadedWeight.Values(LoadedWeight.Values.Length - 1).WeightValue = CDec(BiteQuery(QueryList))
End While
End Sub
These subroutines should transform a properly formatted string into a WeightElement array. The string I am using is:
1,2,0,5,Black,26,1,White,30,1,EastAsian,27,1,SouthAsian,28,1,MiddleEastern,29,1;3,4,25,3,N/A,0,1;28,5,6,13,N/A,0,1;-1,7,0,1,Male,16,1,Female,17,1,Non-Binary,18,1;-1,-1,8,4,N/A,0,1;-1,34,9,15,N/A,0,1;10,11,0,28,Light,-1,1,Medium,-1,1;-1,-1,12,2,N/A,0,1;13,14,0,21,Andro,19,1,Gyno,20,1,A,21,1,Bi,22,1,Pan,23,1;15,16,0,28,Straight,-1,1,Wavy,-1,1,Curly,-1,1;-1,17,0,26,Light,-1,1,Medium,-1,1,Dark,-1,1;18,-1,0,30,Pale,-1,1,Light,-1,1;19,20,0,17,Male,31,1,Female,32,1,Intersex,33,1;-1,21,0,19,Andro,19,1,Gyno,20,1,A,21,1,Bi,22,1,Pan,23,1;22,-1,0,23,Andro,19,1,Gyno,20,1,A,21,1,Bi,22,1,Pan,23,1;-1,23,0,26,Curly,-1,1,Coily,-1,1;24,-1,0,30,Straight,-1,1,Wavy,-1,1,Curly,-1,1;-1,-1,0,27,Pale,-1,1,Light,-1,1,Medium,-1,1;-1,-1,0,29,Pale,-1,1,Light,-1,1,Medium,-1,1;-1,-1,0,16,Male,31,1,Female,32,1,Intersex,33,1;-1,-1,0,18,Male,31,1,Female,32,1,Intersex,33,1;-1,-1,0,20,Andro,19,1,Gyno,20,1,A,21,1,Bi,22,1,Pan,23,1;-1,-1,0,22,Andro,19,1,Gyno,20,1,A,21,1,Bi,22,1,Pan,23,1;-1,-1,0,27,Straight,-1,1,Wavy,-1,1;-1,-1,0,29,Straight,-1,1,Wavy,-1,1,Curly,-1,1;26,27,0,17,Andro,19,1,Gyno,20,1,A,21,1,Bi,22,1,Pan,23,1;-1,-1,0,16,Andro,19,1,Gyno,20,1,A,21,1,Bi,22,1,Pan,23,1;-1,-1,0,18,Andro,19,1,Gyno,20,1,A,21,1,Bi,22,1,Pan,23,1;29,30,0,8,Albinism,-1,1,ChronicFatigue,-1,1,Dwarfism,-1,1;-1,31,0,6,Autistic,-1,1,ADHD,-1,1,OCD,-1,1,Tourrette,-1,1,Epilepsy,-1,1;32,33,35,10,N/A,0,1;-1,-1,0,7,OneArmAbsent,-1,1,BothArmsAbsent,-1,1,OneHandwithAnomalies,-1,1,BothHandswithAnomalies,-1,1,OneLegAbsent,26,1,BothLegsAbsent,26,1;-1,-1,36,9,N/A,0,1;-1,-1,0,11,GoodHearing,-1,1,HardofHearing,-1,1,Deaf,-1,1,NoHearing,-1,1;-1,-1,0,34,Scars,-1,1,NaturalAnomalies,-1,1,OneEyeAbsent,-1,1,BothEyesAbsent,-1,1;-1,37,0,0,GoodVision,-1,1,Blind,-1,1,NoVision,-1,1;-1,38,0,0,Unaided,-1,1,Cane,-1,1,Wheelchair,-1,1,Full-TimeWheelchair,-1,1;-1,-1,0,25,N/A,0,1;-1,-1,0,26,Unaided,-1,1,Cane,-1,1,Wheelchair,-1,1,Full-TimeWheelchair,-1,1
The format is that the first four values between each semicolon are the low pointer, high pointer, trait pointer, and Number, in that order. After this point are the values, with the trait name, trait number, and weight value, repeating in that order
When I use this string, the program decides to return a WeightElement array of the correct length, but where all the values are 0 or empty strings. How do I make it produce a proper WeightElement, as by the format above?

Related

Get a specific value from the line in brackets (Visual Studio 2019)

I would like to ask for your help regarding my problem. I want to create a module for my program where it would read .txt file, find a specific value and insert it to the text box.
As an example I have a text file called system.txt which contains single line text. The text is something like this:
[Name=John][Last Name=xxx_xxx][Address=xxxx][Age=22][Phone Number=8454845]
What i want to do is to get only the last name value "xxx_xxx" which every time can be different and insert it to my form's text box
Im totally new in programming, was looking for the other examples but couldnt find anything what would fit exactly to my situation.
Here is what i could write so far but i dont have any idea if there is any logic in my code:
Dim field As New List(Of String)
Private Sub readcrnFile()
For Each line In File.ReadAllLines(C:\test\test_1\db\update\network\system.txt)
For i = 1 To 3
If line.Contains("Last Name=" & i) Then
field.Add(line.Substring(line.IndexOf("=") + 2))
End If
Next
Next
End Sub
Im
You can get this down to a function with a single line of code:
Private Function readcrnFile(fileName As String) As IEnumerable(Of String)
Return File.ReadLines(fileName).Where(Function(line) RegEx.IsMatch(line, "[[[]Last Name=(?<LastName>[^]]+)]").Select(Function(line) RegEx.Match(line, exp).Groups("LastName").Value)
End Function
But for readability/maintainability and to avoid repeating the expression evaluation on each line I'd spread it out a bit:
Private Function readcrnFile(fileName As String) As IEnumerable(Of String)
Dim exp As New RegEx("[[[]Last Name=(?<LastName>[^]]+)]")
Return File.ReadLines(fileName).
Select(Function(line) exp.Match(line)).
Where(Function(m) m.Success).
Select(Function(m) m.Groups("LastName").Value)
End Function
See a simple example of the expression here:
https://dotnetfiddle.net/gJf3su
Dim strval As String = " [Name=John][Last Name=xxx_xxx][Address=xxxx][Age=22][Phone Number=8454845]"
Dim strline() As String = strval.Split(New String() {"[", "]"}, StringSplitOptions.RemoveEmptyEntries) _
.Where(Function(s) Not String.IsNullOrWhiteSpace(s)) _
.ToArray()
Dim lastnameArray() = strline(1).Split("=")
Dim lastname = lastnameArray(1).ToString()
Using your sample data...
I read the file and trim off the first and last bracket symbol. The small c following the the 2 strings tell the compiler that this is a Char. The braces enclosed an array of Char which is what the Trim method expects.
Next we split the file text into an array of strings with the .Split method. We need to use the overload that accepts a String. Although the docs show Split(String, StringSplitOptions), I could only get it to work with a string array with a single element. Split(String(), StringSplitOptions)
Then I looped through the string array called splits, checking for and element that starts with "Last Name=". As soon as we find it we return a substring that starts at position 10 (starts at zero).
If no match is found, an empty string is returned.
Private Function readcrnFile() As String
Dim LineInput = File.ReadAllText("system.txt").Trim({"["c, "]"c})
Dim splits = LineInput.Split({"]["}, StringSplitOptions.None)
For Each s In splits
If s.StartsWith("Last Name=") Then
Return s.Substring(10)
End If
Next
Return ""
End Function
Usage...
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
TextBox1.Text = readcrnFile()
End Sub
You can easily split that line in an array of strings using as separators the [ and ] brackets and removing any empty string from the result.
Dim input As String = "[Name=John][Last Name=xxx_xxx][Address=xxxx][Age=22][Phone Number=8454845]"
Dim parts = input.Split(New Char() {"["c, "]"c}, StringSplitOptions.RemoveEmptyEntries)
At this point you have an array of strings and you can loop over it to find the entry that starts with the last name key, when you find it you can split at the = character and get the second element of the array
For Each p As String In parts
If p.StartsWith("Last Name") Then
Dim data = p.Split("="c)
field.Add(data(1))
Exit For
End If
Next
Of course, if you are sure that the second entry in each line is the Last Name entry then you can remove the loop and go directly for the entry
Dim data = parts(1).Split("="c)
A more sophisticated way to remove the for each loop with a single line is using some of the IEnumerable extensions available in the Linq namespace.
So, for example, the loop above could be replaced with
field.Add((parts.FirstOrDefault(Function(x) x.StartsWith("Last Name"))).Split("="c)(1))
As you can see, it is a lot more obscure and probably not a good way to do it anyway because there is no check on the eventuality that if the Last Name key is missing in the input string
You should first know the difference between ReadAllLines() and ReadLines().
Then, here's an example using only two simple string manipulation functions, String.IndexOf() and String.Substring():
Sub Main(args As String())
Dim entryMarker As String = "[Last Name="
Dim closingMarker As String = "]"
Dim FileName As String = "C:\test\test_1\db\update\network\system.txt"
Dim value As String = readcrnFile(entryMarker, closingMarker, FileName)
If Not IsNothing(value) Then
Console.WriteLine("value = " & value)
Else
Console.WriteLine("Entry not found")
End If
Console.Write("Press Enter to Quit...")
Console.ReadKey()
End Sub
Private Function readcrnFile(ByVal entry As String, ByVal closingMarker As String, ByVal fileName As String) As String
Dim entryIndex As Integer
Dim closingIndex As Integer
For Each line In File.ReadLines(fileName)
entryIndex = line.IndexOf(entry) ' see if the marker is in our line
If entryIndex <> -1 Then
closingIndex = line.IndexOf(closingMarker, entryIndex + entry.Length) ' find first "]" AFTER our entry marker
If closingIndex <> -1 Then
' calculate the starting position and length of the value after the entry marker
Dim startAt As Integer = entryIndex + entry.Length
Dim length As Integer = closingIndex - startAt
Return line.Substring(startAt, length)
End If
End If
Next
Return Nothing
End Function

New to programming language, need help to read a .dat file into array Vs VB, console .net framework

I am trying to read from a data file called TXT.dat and store the circled values into a separate array's using the type of commands I have used. I cannot use streamreader,streamwriter this is what I am learning at university but we were taught to read an array not certain values as I will be tested using a file which has
2 factorial values from a single file and store int two arrays.
I have spent hours trying before going to paid sites who always answer with i/o streamreader which doesnt help
.dat file i created to practice
MY CODE
Sub Main()
Const i_val As Integer = 6
Dim j As Integer = 6 'loop readers
Dim Arayn_Fact(i_val - 1) As Double 'array for 2nd value per line
Dim Aray_Fact2n(j - 1) As Double 'array for 4th value per line
Read_Values(i_val - 1, Arayn_Fact)
End Sub
Sub Read_Values(ByVal i As Integer, ByRef _A() As Double)
Dim fid1 As Integer = FreeFile()
Dim fid2 As Integer = FreeFile() + 1
Dim tmp As Double
FileOpen(fid1, "TXT.dat", OpenMode.Input, OpenAccess.Read)
For i = 0 To 5 Step 1
Input(fid1, tmp)
Input(fid1, _A(i))
Next i
FileClose(fid1)
Console.ReadKey()
End Sub
Without getting too fancy, here's some very basic code to do what you've outlined:
Sub Main()
Dim fileName As String = "txt.dat"
Dim Aray_Fact2() As Integer 'array for 2nd value per line
Dim Aray_Fact4() As Integer 'array for 4th value per line
Dim list2 As New List(Of Integer)
Dim list4 As New List(Of Integer)
Dim lines() As String = System.IO.File.ReadAllLines(fileName)
For Each line As String In lines
Dim values() As String = line.Split(",")
If values.Length >= 3 Then
Dim f2, f4 As Integer
If Integer.TryParse(values(1), f2) AndAlso Integer.TryParse(values(3), f4) Then
list2.Add(f2)
list4.Add(f4)
Else
Console.WriteLine("Invalid value in line: " & line)
End If
Else
Console.WriteLine("Invalid number of entries in line: " & line)
End If
Next
Aray_Fact2 = list2.ToArray
Aray_Fact4 = list4.ToArray
Console.WriteLine("Aray_Fact2: " & String.Join(",", Aray_Fact2))
Console.WriteLine("Aray_Fact4: " & String.Join(",", Aray_Fact4))
Console.Write("Press Enter to Quit...")
Console.ReadLine()
End Sub
If you are completely against the use of Lists and ToArray, then you'd have to read the file TWICE. Once to determine how many lines are in the file so you can dimension the arrays to the correct size. Then another time to actually read the values and populate the arrays.
First, some style things. Passing the array ByRef is incorrect. Arrays are already reference types, and so passing ByVal still passes the reference to the array object. But don't even pass the array in the first place. It's better practice to let the Read_Values() method create and return the array to the calling function.
And no one uses that ancient/ganky FileOpen() API anymore. I mean that. NO. ONE. It's not even appropriate for teaching, as the Stream types are closer to what the low level operating system/file system are doing. If you can't use StreamReader and friends, there are still better options out there.
Sub Main()
Dim Array_Fact() As Double = Read_Values("TXT.dat", 1)
Dim Array_Fact2n() As Double = Read_Values("TXT.dat", 3)
Console.ReadKey(True)
End Sub
Function Read_Values(filePath As String, position As Integer) As Double()
Dim lines() As String = File.ReadAllLines(filePath) 'Look Ma, no StreamReader
Dim result(lines.Length - 1) As Double
For i As Integer = 0 To Lines.Length - 1
result(i) = Double.Parse(lines(i).Split(",")(position).Trim())
Next line
Return result
End Function
But what I'd really do is keep the 2nd and 4th values in the same collection and read everything in one pass through the file. This uses features like Generics, Tuples, Lambdas+Linq, and more that you won't get to for some time, but I feel like it's useful to show you where you're going:
Sub Main()
Dim Facts = Read_Values("TXT.dat")
Console.WriteLine(String.Join(",",Facts.Select(Function(f) $"({f.Item1}:{f.Item2})")))
Console.ReadKey(True)
End Sub
Function Read_Values(filePath As String) As IEnumerable(Of (Double, Double))
Return File.ReadLines(filePath).Select(
Function(line) 'Poor man's CSV. In real code, I'd pull in a CSV parser from NuGet
Dim fields = line.Split(",")
Return (Double.Parse(fields(1).Trim()), Double.Parse(fields(3).Trim()))
End Function
)
End Function

two-dimensional array to an array in visual basic

I've got a question about using two-dimensional array.
Public twolist(,) As String
For i As Integer = 0 To twolist.length()-1
If Func(twolist(i, )) Then 'this part is hard for me
'doing something
End If
Public Function Func(ByVal CancelInput() As String) As Boolean
What i want to do is Passing two-dimensional array to an array.
I want to read one row in two-dimensional array and pass to function(Func), which is using an array.
Hope You can understand my question... and Thank you!
As an alternative to the For Next Loop, you could use Linq (if you are confortable with it) to perform the same task.
This transforms each element of the source array to a String, groups them in an IEnumerable(Of String) and the result is converted to an unidimensional Array of Strings:
Dim twolist(N, N) As String
Dim CancelInput() As String = twolist.Cast(Of String).Select(Function(str) str).ToArray()
Dim result As Boolean = Func(CancelInput)
I have just used an arbitrary size for your array. You need nested For loops to iterate through a 2 dimensional array. The outer loop goes through the rows and the inner loop adds the value in each field to another array that you are passing to your Function. Each row is passed individually as a single dimension array.
Private Sub TwoDimensionalArray()
Dim twolist(,) As String
ReDim twolist(10, 5)
'First you will need to add data to your array
For x As Integer = 0 To 10
Dim arrayRow(5) As String
For y As Integer = 0 To 5
arrayRow(y) = twolist(x, y)
Next
If Func(arrayRow) Then 'this part is hard for me
'doing something
End If
Next
End Sub
Public Function Func(ByVal CancelInput() As String) As Boolean
Return True
End Function
Mary's answer is good, but assumes you know the length of each dimension.
I have changed it slightly to use the Array.GetLength function:
Private Sub TwoDimensionalArray()
Dim twolist(,) As String
ReDim twolist(10, 5)
'First you will need to add data to your array
For x As Integer = 0 To 10
'Fetch the length of this dimension:
Dim i As Integer = twolist.GetLength(x)
Dim arrayRow(i) As String
For y As Integer = 0 To i - 1
arrayRow(y) = twolist(x, y)
Next
If Func(arrayRow) Then
'do something
End If
Next
End Sub
Public Function Func(ByVal CancelInput() As String) As Boolean
Return True
End Function
Note:
In VB.Net, ReDim twoList(10,5) actually gives you an array of (11,6).
Array.GetLength(0) will return 6 (0,1,2,3,4,5).
In short, Dim specifies the maximum index in each dimension, Length & GetLength return the count of elements.

VB-2008 How to retrieve strings with largest numerical suffix in an array?

I got the following strings arrays
a.csv
a-1.csv
a-2.csv
a-3.csv
ab.csv
ab-1.csv
ab-2.csv
cccc.csv
cccc-1.csv
d.csv
In this strings arrays, it stores a series of test data, the naming conversion for each test data is as follows,
'unit name'+'-'+('suffix in single digit number')+'.csv'
The length of the unit name could be any length, but the suffix can only be single-digit number (1-9). For example for unit a, it undergoes 4 test in total, which are a.csv, a-1.csv, a-2.csv and a-3.csv. But because all of the first three test fails the specs, therefore only the last test data will be retrieved for analysis, which is a-3.csv.
So, for the final output, I only need those data which have the largest suffix for each unit, which gives me the output string array:
a-3.csv
ab-2.csv
cccc-1.csv
d.csv
How should I choose the correct files from the input string array according to the rules to get those test data only got the largest suffix.
Private Function Suffix(ByVal s As String) As String
s = IO.Path.GetFileNameWithoutExtension(s)
Dim pos = s.LastIndexOf("-"c)
Return If(pos = -1, s, s.Substring(0, pos))
End Function
Private Function Value(ByVal s As String) As Integer
s = IO.Path.GetFileNameWithoutExtension(s)
Dim pos = s.LastIndexOf("-"c)
Return If(pos = -1, 0, Integer.Parse(s.Substring(pos + 1)))
End Function
Dim arr() As String = New String() {"a.csv", "a-1.csv", "a-2.csv", "a-3.csv", "ab.csv", "ab-1.csv", "ab-2.csv", "cccc.csv", "cccc-1.csv", "d.csv"}
Dim largest = arr.GroupBy(Function(s) Suffix(s), StringComparer.OrdinalIgnoreCase) _
.Select(Function(g) g.OrderByDescending(Function(s) Value(s)).First())
For Each s In largest
Console.WriteLine(s)
Next
Though your question is not clear but I think you want to have the latest occurrence to be stored in a separate array. Here is how you can achieve that:
Dim arrayInput As List(Of String) = New List(Of String)
arrayInput.Add("a.csv")
arrayInput.Add("a-1.csv")
arrayInput.Add("a-2.csv")
arrayInput.Add("a-3.csv")
arrayInput.Add("b.csv")
arrayInput.Add("b-1.csv")
arrayInput.Add("b-2.csv")
arrayInput.Add("c.csv")
arrayInput.Add("c-1.csv")
arrayInput.Add("d.csv")
Dim arrayOutput As List(Of String) = New List(Of String)
Dim strTemp As String = arrayInput(0)
Dim startingChar As String = strTemp.Substring(0, 1)
For Each item As String In arrayInput
If Not startingChar.Equals(item.Substring(0, 1), StringComparison.OrdinalIgnoreCase) Then
arrayOutput.Add(strTemp)
startingChar = item.Substring(0, 1)
End If
strTemp = item
Next
arrayOutput.Add(strTemp)
You have the required result in arrayOutput. Let me know if you wanted something else instead.

build an array of integers inside a for next loop vb.net

i got this far ... my data string, "num_str" contains a set of ~10 numbers, each separated by a comma. the last part of the string is a blank entry, so i use '.Trim' to avoid an error
Dim i As Integer
Dim m_data() As String
m_data = num_str.Split(",")
For i = 0 To UBound(m_data)
If m_data(i).Trim.Length > 0 Then
MsgBox(Convert.ToInt32(m_data(i).Trim))
End If
Next i
as you can see from the Msgbox, each of the numbers successfully pass through the loop.
where i am stuck is how to place all of the 'Convert.ToInt32(m_data(i).Trim)' numbers, which are now presumably integers, into an array.
how do i build an array of integers inside the For / Next loop so i can find MAX and MIN and LAST
TIA
You just need to initialize the array with the zero-based indexer. You can deduce it's initial size from the size of the string():
Dim m_data = num_str.Split({","c}, StringSplitOptions.RemoveEmptyEntries)
Dim intArray(m_data.Length) As Int32
For i = 0 To m_data.Length - 1
intArray(i) = Int32.Parse(m_data(i).Trim())
Next i
Note that i've also used the overload of String.Split which removes empty strings.
This way is more concise using the Select LINQ Operator.
Dim arrayOfInts = num_str.
Split({","c},
StringSplitOptions.RemoveEmptyEntries).
Select(Function(v) Int32.Parse(v.Trim()))
Dim minInt = arrayOfInts.Min()
Dim maxint = arrayOfInts.Max()