VB.NET - Reading a text file containing tab-separated integers/doubles and storing them in arrays - vb.net

I have a text file containing tab-seperated integers and doubles, e.g.:
5[TAB]0.3[TAB]2.9[TAB]61[TAB]110
8[TAB]1.1[TAB]5.2[TAB]13[TAB]45
1[TAB]0.8[TAB]1.4[TAB]28[TAB]33
...
What I need is a convenient way to access each line as a double array in VB.NET - so that the first array would be [5.0 0.3 2.9 61.0 110.0], the second array would be [8.0 1.1 5.2 13.0 45.0], and so on...
How can this be accomplished using the StreamReader?

If it's ok to use a list of lists instead of a list of arrays, you can just do this:
Private Function LoadFile(ByVal filePath As String) As List(Of List(Of Double))
Dim records As New List(Of List(Of Double))()
For Each line As String In File.ReadAllLines(filePath)
Dim values As New List(Of Double)()
For Each field As String In line.Split(New String() {ControlChars.Tab}, StringSplitOptions.None)
values.Add(Double.Parse(field))
Next
records.Add(values)
Next
Return records
End Function
Or, if it must be a list of arrays, you could do this:
Private Function LoadFileToArrays(ByVal filePath As String) As List(Of Double())
Dim records As New List(Of Double())()
For Each line As String In File.ReadAllLines(filePath)
Dim values As New List(Of Double)()
For Each field As String In line.Split(New String() {ControlChars.Tab}, StringSplitOptions.None)
values.Add(Double.Parse(field))
Next
records.Add(values.ToArray())
Next
Return records
End Function
If you need an array of arrays, you could just return records.ToArray(), instead, in that last example. I did not add any code to handle invalid or empty field values, because it wasn't clear, in your question, how you would want to handle those. So, you'll want to add code to handle that appropriately, otherwise, this code will throw an exception in such cases.

Related

2-D array from txt in VB.NET

I am needing to create a code that is versatile enough where I can add more columns in the future with minimum reconstruction of my code. My current code does not allow me to travel through my file with my 2-D array. If I was to change MsgBox("map = "+ map(0,1) I can retrieve the value easily. Currently all I get in the code listed is 'System.IndexOutOfRangeException' and that Index was outside the bounds of the array. My current text file is 15 rows (down) and 2 columns (across) which puts it at a 14x1. they are also comma separated values.
Dim map(14,1) as string
Dim reader As IO.StreamReader
reader = IO.File.OpenText("C:\LocationOfTextFile")
Dim Linie As String, x,y As Integer
For x = 0 To 14
Linie = reader.ReadLine.Trim
For y = 0 To 1
map(x,y) = Split(Linie, ",")(y)
Next 'y
Next 'x
reader.Close()
MsgBox("map = " + map(y,x))``
Here's a generic way to look at reading the file:
Dim data As New List(Of List(Of String))
For Each line As String In IO.File.ReadAllLines("C:\LocationOfTextFile")
data.Add(New List(Of String)(line.Split(",")))
Next
Dim row As Integer = 1
Dim col As Integer = 10
Dim value As String = data(row)(col)
This is the method suggested by Microsoft. It is generic and will work on any properly formatted comma delimited file. It will also catch and display any errors found in the file.
Using MyReader As New Microsoft.VisualBasic.
FileIO.TextFieldParser(
"C:\LocationOfTextFile")
MyReader.TextFieldType = FileIO.FieldType.Delimited
MyReader.SetDelimiters(",")
Dim currentRow As String()
While Not MyReader.EndOfData
Try
currentRow = MyReader.ReadFields()
Dim currentField As String
For Each currentField In currentRow
MsgBox(currentField)
Next
Catch ex As Microsoft.VisualBasic.
FileIO.MalformedLineException
MsgBox("Line " & ex.Message &
"is not valid and will be skipped.")
End Try
End While
End Using
Essentially what you are asking is how can I take the contents of comma-separated values and convert this to a 2D array.
The easiest way, which is not necessarily the best way, is to return an IEnuemrable(Of IEnumerable(Of String)). The number of items will grow both vertically based on the number of lines and the number of items will grow horizontally based on the values split on a respective line by a comma.
Something along these lines:
Private Function GetMap(path As String) As IEnumerable(Of IEnumerable(Of String)
Dim map = New List(Of IEnumerable(Of String))()
Dim lines = IO.File.ReadAllLines(path)
For Each line In lines
Dim row = New List(Of String)()
Dim values = line.Split(","c)
row.AddRange(values)
map.Add(row)
Next
Return map
End Function
Now when you want to grab a specific cell using the (row, column) syntax, you could use:
Private _map As IEnumerable(Of IEnumerable(Of String))
Private Sub LoadMap()
_map = GetMap("C:/path-to-map")
End Sub
Private Function GetCell(row As Integer, column As Integer) As String
If (_map Is Nothing) Then
LoadMap()
End If
Return _map.ElementAt(row).ElementAt(column)
End Function
Here is an example: https://dotnetfiddle.net/ZmY5Ki
Keep in mind that there are some issues with this, for example:
What if you have commas in your cells?
What if you try to access a cell that doesn't exist?
These are considerations you need to make when implementing this in more detail.
You can consider the DataTable class for this. It uses much more memory than an array, but gives you a lot of versatility in adding columns, filtering, etc. You can also access columns by name rather than index.
You can bind to a DataGridView for visualizing the data.
It is something like an in-memory database.
This is much like #Idle_Mind's suggestion, but saves an array copy operation and at least one allocation per row by using an array, rather than a list, for the individual rows:
Dim data = File.ReadLines("C:\LocationOfTextFile").
Select(Function(ln) ln.Split(","c)).
ToList()
' Show last row and column:
Dim lastRow As Integer = data.Count - 1
Dim lastCol As Integer = data(row).Length - 1
MsgBox($"map = {data(lastRow)(lastCol)}")
Here, assuming Option Infer, the data variable will be a List(Of String())
As a step up from this, you could also define a class with fields corresponding to the expected CSV columns, and map the array elements to the class properties as another call to .Select() before calling .ToList().
But what I really recommend is getting a dedicated CSV parser from NuGet. While a given CSV source is usually consistent, more broadly the format is known for having a number of edge cases that can easily confound the Split() function. Therefore you tend to get better performance and consistency from a dedicated parser, and NuGet has several good options.

I am trying to figure out how to read a text file into an array in vb.net

I'm trying to create a program in vb.net (forms) to process data from a UVvis spectrometer.
The txt file output looks as following.
"180809_QuartzRefTrans.spc - RawData"
"Wavelength nm.","T%"
400.00,90.822
401.00,90.800
402.00,90.823
403.00,90.811
404.00,90.803
405.00,90.804
406.00,90.816
407.00,90.811
408.00,90.833
409.00,90.837
410.00,90.847
411.00,90.827
412.00,90.839
413.00,90.851
414.00,90.828
415.00,90.879
416.00,90.846
and so on.
What I am trying to do is read the data it into an array so I can manipulate the columns. I need to be able to skip the first two lines so that all I have is numerical data. I also need it to sort the array from lowest to highest (wavelengths). Sometimes we run from 800->200 nm then accidentally put in 200->800 nm
Imports System.IO
Imports System.Windows.Forms.DataVisualization.Charting
Public Class Form1
Public Class RefTrans
Public Property Wavelength As Double
Public Property Transpercent As Double
End Class
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim strtext As String
OpenFileDialog1.Title = "Open Text Files"
OpenFileDialog1.ShowDialog()
strtext = OpenFileDialog1.FileName
TextBox1.Text = My.Computer.FileSystem.GetName(strtext)
Label1.Text = My.Computer.FileSystem.GetName(strtext)
Dim line1 As String
Dim output1 As New ArrayList
Using sr As New IO.StreamReader(strtext)
sr.ReadLine()
sr.ReadLine()
Do While sr.Peek() >= 0
line1 = sr.ReadLine()
output1.Add(line1)
Loop
End Using
If strtext <> "" Then
Dim SR As New StreamReader(strtext)
SR.ReadLine()
Do Until SR.EndOfStream
TextBox3.Text = TextBox3.Text & SR.ReadLine & vbCrLf
Loop
SR.Close()
End If
Dim data1 = IO.File.ReadLines(strtext).
Skip(2).
Select(Function(line)
Dim parts = line.Split(","c)
Return New RefTrans With {.Wavelength = CDbl(parts(0)),
.Transpercent = CDbl(parts(1))}
End Function).
ToArray() = line.Split(","c)
End Sub
End Class
Here's an example that will get you an array of Tuple(Of Double, Double):
Dim data = IO.File.ReadLines(filePath).
Skip(2).
Select(Function(line)
Dim parts = line.Split(","c)
Return Tuple.Create(CDbl(parts(0)), CDbl(parts(1)))
End Function).
ToArray()
The IO.File.ReadLines method will read the lines of the file. Where the ReadAllLines method reads the entire file and then returns the lines as a String array, the ReadLines method exposes the lines of the file as an enumerable list and the lines only get read from the file as you use them. Particularly for big files, ReadLines is more efficient than ReadAllLines because it does create an intermediate array first that you don't actually want.
The Skip method allows you to access the items in a list starting at a particular index. In the case of reading a file, you need to read every line regardless but call Skip(2) on the result of File.ReadLines means that the first two lines of the file will be discarded after reading and any subsequent processing will only but done on lines from the third.
The Select method is basically a transformation. It says create an output list containing an item for every item in the input list where the output item is the result of a transformation of the input item. The transformation is defined by the function you provide. In this case, a line from the file is the input and the transformation is to split that line on the comma, convert the two substrings to Double values and then create a Tuple containing those two Double values.
The ToArray method takes any enumerable list, i.e. any object that implements IEnumerable(Of T), and returns an array containing the items from that list. In this case, Select returns an IEnumerable(Of Tuple(Of Double, Double)) so ToArray returns a Tuple(Of Double, Double) array.
If you wanted to write that all out long-hand then it would look like this:
'Get all the lines of the file.
Dim step1 As IEnumerable(Of String) = IO.File.ReadLines(filePath)
'Skip the first two lines.
Dim step2 As IEnumerable(Of String) = step1.Skip(2)
'Split each line into two substrings.
Dim step3 As IEnumerable(Of String()) = step2.Select(Function(line) line.Split(","c))
'Convert substrings to numbers and combine.
Dim step4 As IEnumerable(Of Tuple(Of Double, Double)) = step3.Select(Function(parts) Tuple.Create(CDbl(parts(0)), CDbl(parts(1))))
'Create an array of Tuples.
Dim data As Tuple(Of Double, Double)() = step4.ToArray()
A Tuple is basically a general-purpose object for grouping values together. You can use a Tuple for one-off cases rather than defining your own class. You may prefer to define your own class in all cases though, and you should define your own class where you will use the data extensively. In this case, your own class might look like this:
Public Class RefTrans
Public Property Wavelength As Double
Public Property TransPercent As Double
End Class
and the code would then become:
Dim data = IO.File.ReadLines(filePath).
Skip(2).
Select(Function(line)
Dim parts = line.Split(","c)
Return New RefTrans With {.Wavelength = CDbl(parts(0)),
.TransPercent = CDbl(parts(1))}
End Function).
ToArray()
In that case, you have properties Wavelength and TransPercent on each element of your array. If you use Tuples then the properties have the generic names Item1 and Item2.
Once you have the array, you can use any appropriate overload of Array.Sort to do the sorting, e.g.
Array.Sort(data, Function(d1, d2) d1.Item1.CompareTo(d2.Item1))
That will sort the Tuples by comparing their Item1 properties, which contain the wavelength values. If you're using your own class then obviously you specify your own property, e.g. the Wavelength property in the RefTrans class I showed.

Splitting string by two conditions in vb.net 2014

I'm trying to split a string into a list of strings with this vb.net code, while adding it to a dictionary of String:
objevt.wbStr.Add(b, objevt.metalbelowwidth.Item(b).Split({",","-"}).ToList)
But I get an error saying "Value of '1-dimentitional arrary of string' cannot be converted to 'Char' "
If I try to implement splitting with just the comma, it works fine. So the following code works.
objevt.wbStr.Add(b, objevt.metalbelowwidth.Item(b).Split(",").ToList)
But I really want to split the string with two conditions. Any help is appreciated. Thanks!
One of the overloads of the Split Method that takes a String array also needs to have a StringSplitOption parameter also set.
objevt.wbStr.Add(b, objevt.metalbelowwidth.Item(b).Split(New String() {",", "-"}, StringSplitOptions.None).ToList)
Sub Main()
Dim myString As String = "H,c,J-Hello-World"
Dim myList As List(Of String) = New List(Of String)
myList = myString.Split(New String() {"-", ","}, StringSplitOptions.None).ToList
For Each s As String In myList
Console.WriteLine(s)
Next
Console.ReadLine()
End Sub
You need to use the overloaded Split method with an array of char as the separators, like so:
objevt.metalbelowwidth.Item(b).Split(New [Char]() {","c, "-"c })
Demo here

Reading data to multidimensional array

I have to read data from database to multidimensional array of unknown size.
Dim myarray As String()
Using reader = mcmd.ExecuteReader()
While (reader.Read())
myarray = TryCast(reader("mydataarray"), String())
End While
End Using
Here I got in myarray array of strings of last readed row only.
How to get that in 'myarray' will be readed data of all rows from sql result?
Here is code to do what I think you mean:
Using reader = mcmd.ExecuteReader()
Dim myOuterList as New List(of String())
While (reader.Read())
Dim myInnerList as New List(of String)
'For loop retrieves all columns of data as string
For i = 0 to reader.FieldCount - 1
myInnerList.Add(reader.GetString(i))
Next
myOuterList.Add(myInnerList.ToArray)
End While
End Using
dim myarray = myOuterList.ToArray
Resulting myarray will be of type String()() I.E. a two-dimensional array of strings.
Though this is possible, I would seriously consider using a typed solution as it will be much easier to understand code dealing with a list of a type than a two-dimensional array.
The typed version would be something like this:
Dim myList As List(Of myType) = reader.OfType(Of IDataRecord) _
.Select(Function(data) New myType _
With {.mydataarray = data.item("mydataarray")})
'Additionaly properties if needed.

String() variable in VB

I'm trying to modify a program where there is a variable that stores all the specified file types in a String() variable. What I would like to do is to somehow append to this variable in any way if I want to search another directory or just grab another individual file. Any suggestions would be greatly appreciated.
//Grab files from a directory with the *.txt or *.log as specified in the Combo Box
Dim strFiles As String()
strFiles = System.IO.Directory.GetFiles(txtSource.Text, cmbtype.SelectedItem, IO.SearchOption.AllDirectories)
EDIT: Edited to include code snippet used.
Dim strFiles As String()
Dim listFiles As List(Of String)(strFiles)
If (cmbtype.SelectedItem = "All") Then
//Do stuff
For index As Integer = 1 To cmbtype.Items.Count - 1
Dim strFileTypes As String() = System.IO.Directory.GetFiles(txtSource.Text, cmbtype.Items(index), IO.SearchOption.AllDirectories)
Next
//Exit Sub
Else
listFiles.Add(System.IO.Directory.GetFiles(txtSource.Text, cmbtype.SelectedItem, IO.SearchOption.AllDirectories).ToString())
End If
Right now you're using a String() which is an array of String instances. Arrays are not well suited for dynamically growing structures. A much better type is List(Of String). It is used in very similar manners to a String() but has a handy Add and AddRange method for appending data to the end.
Dim strFiles As New List(Of String)()
strFiles.AddRange(System.IO.Directory.GetFiles(txtSource.Text, cmbtype.SelectedItem, I
O.SearchOption.AllDirectories)
dim listFiles as list(of string)
listFiles = System.IO.Directory.GetFiles(txtSource.Text, cmbtype.SelectedItem, IO.SearchOption.AllDirectories).ToList()
listFiles.Add("..\blah\...\")