VB.NET: How to find a word after a specific word in a line and store it - vb.net

I have a continuous string of words coming from a machine to hyper terminal of my system, for which I am using USB to serial cable. I want to find some of the values which comes after a specific word in that string and then store it.
I used threads and splitting concepts to do it but as per the requirement and operation of the machine it will not work properly in the runtime.
The values which I want to capture comes from a specific word. I want to skip that words and just store the values. How to do it?
I have given the example of that string below:
MEAN 49 50
SD 500 10
MIN 100 5
MAX 50 45.56
In this I just want to store the values e.g. 49 and 50, then discard MEAN. Then discard SD and store 500 and 10 and so on.

You can use a StreamReader object to read the stream one line at a time. Then, you can easily parse the line using the String.Split method. I would recommend creating one or more classes that represent the data being read, like this:
Public Class LineData
Public Property Label As String
Public Property Value1 As Decimal
Public Property Value2 As Decimal
End Class
Public Function ReadNextLine(stream As Stream) As LineData
Dim reader As New StreamReader(stream)
Dim line As String = reader.ReadLine()
Dim data As LineData = Nothing
If line IsNot Nothing Then
Dim words() As String = line.Split(New Char() {" "c}, StringSplitOptions.RemoveEmptyEntries)
If words.Length = 3 Then
data = New LineData()
data.Label = words(0)
data.Value1 = Decimal.Parse(words(1))
data.Value2 = Decimal.Parse(words(2))
End If
End If
Return Data
End Function
Note, this is a very simple example based on the example data you provided. If different lines have different numbers of numeric parameters, that will further complicate the logic. In my example, the method returns Nothing if no data can be read. Also, the method will throw an exception if the last two words in the line are not numeric. Therefore, you would need to wrap it in some additional exception handling.

This may be what you're looking for. Although personally I'd create a class which stores the type(mean, median etc), firstvalue and secondvalue.
By the sounds of it though, all you want it to do is dump the values into some sort of storage, therefore this will suffice.
Dim Values as New List(Of Decimal)
'Use a streamreader to read each line of text
Using reader As StreamReader = New StreamReader(*your text source*)
'Read the line
Dim linetext as string = reader.ReadLine
Dim myValue as decimal
'Split the line
Dim splitText() = linetext.Split(" ")
'Analyze each section of the line, if its possible to parse the value as a decimal then add it to the list of values to be stored.
For Each txt in splitText
If Decimal.TryParse(txt, myValue) then Values.Add(myValue)
Next
End Using

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.

Is it possible to use String.Split() when NewLine is the delimiter?

I have a question which asks me to calculate something from an input file. The problem is, the lines in the file don't use any special character as delimiter, like , or |. I will show it down below.
Data Communication
20
Visual Basic
40
The output I need to write to another file should look like this:
Data communication 20
Visual Basic 40
Total Books : 60
The problem is, how can I specify the delimiter? Like when there is a symbol as in strArray = strLine.Split(","). Since there is nothing I can use as delimiter, how can I split the file content?
There's no real need to split the text in the input file, when you can read a file line by line using standard methods.
You can use, e.g., a StreamReader to read the lines from the source file, check whether the current line is just text or it can be converted to a number, using Integer.TryParse and excluding empty lines.
Here, when the line read is not numeric, it's added as a Key in a Dictionary(Of String, Integer), unless it already exists (to handle duplicate categories in the source file).
If the line represents a number, it's added to the Value corresponding to the category Key previously read, stored in a variable named previousLine.
This setup can handle initial empty lines, empty lines in the text body and duplicate categories, e.g.,
Data Communication
20
Visual Basic
40
C#
100
Visual Basic
10
Other stuff
2
C++
10000
Other stuff
1
If a number is instead found in the first line, it's treated as a category.
Add any other check to handle a different structure of the input file.
Imports System.IO
Imports System.Linq
Dim basePath = "[Path where the input file is stored]"
Dim booksDict = New Dictionary(Of String, Integer)
Dim currentValue As Integer = 0
Dim previousLine As String = String.Empty
Using sr As New StreamReader(Path.Combine(basePath, "Books.txt"))
While sr.Peek > -1
Dim line = sr.ReadLine().Trim()
If Not String.IsNullOrEmpty(line) Then
If Integer.TryParse(line, currentValue) AndAlso (Not String.IsNullOrEmpty(previousLine)) Then
booksDict(previousLine) += currentValue
Else
If Not booksDict.ContainsKey(line) Then
booksDict.Add(line, 0)
End If
End If
End If
previousLine = line
End While
End Using
Now, you have a Dictionary where the Keys represent categories and the related Value is the sum of all books in that category.
You can Select() each KeyValuePair of the Dictionary and transform it into a string that represents the Key and its Value (Category:Number).
Here, also OrderBy() is used, to order the categories alphabetically, in ascending order; it may be useful.
File.WriteAllLines is then called to store the strings generated.
In the end, a new string is appended to the file, using File.AppendAllText, to write the sum of all books in all categories. The Sum() method sums all the Values in the Dictionary.
Dim newFilePath = Path.Combine(basePath, "BooksNew.txt")
File.WriteAllLines(newFilePath, booksDict.
Select(Function(kvp) $"{kvp.Key}:{kvp.Value}").OrderBy(Function(s) s))
File.AppendAllText(newFilePath, vbCrLf & "Total Books: " & booksDict.Sum(Function(kvp) kvp.Value).ToString())
The output is:
C#:100
C++:10000
Data Communication:20
Other stuff:3
Visual Basic:50
Total Books: 10173
Sure.. System.IO.File.ReadAllLines() will read the whole file and split into an array based on newlines, so you'll get an array of 4 elements. You can process it with a flipflop boolean to get alternate lines, or you can try and parse the line to a number and if it works, then its a number and if not, it's a string. If it's a number take the string you remembered (using a variable) from the previous loop
Dim arr = File.ReadALlLines(...)
Dim isStr = True
Dim prevString = ""
For Each s as String in arr
If isStr Then
prevString = s
Else
Console.WriteLine($"The string is {prevString} and the number is {s}")
End If
'flip the boolean
isStr = Not isStr
Next s
I used File.ReadAllLines to get an array containing each line in the file. Since the size of the file could be larger than the sample shown, I am using a StringBuilder. This save having to throw away and create a new string on each iteration of the loop.
I am using interpolated strings indicated by the $ preceding the quotes. This allows you to insert variables into the string surrounded by braces.
Note the Step 2 in the For loop. i will increment by 2 instead of the default 1.
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim lines = File.ReadAllLines("input.txt")
Dim sb As New StringBuilder
Dim total As Integer
For i = 0 To lines.Length - 2 Step 2
sb.AppendLine($"{lines(i)} {lines(i + 1)}")
total += CInt(lines(i + 1))
Next
sb.AppendLine($"Total Books: {total}")
TextBox1.Text = sb.ToString
End Sub

Read complex text file using Vb.net

File Structure
RECORD
1234567890,123456789,1234567,Address1,,City,State,Zip,USA,Phone,,,,,,,,,
EmpName,DBAName,ID,,Address1,,Address1,City,60603,USA,234567890,,,,
C,03/13/2017,,1,2,
RECORD
1234567890,123456789,1234567,Address1,,City,State,Zip,USA,Phone,,,,,,,,,
EmpName2,DBAName2,ID2,,Address2,,Address2,City2,60603,USA,234567890,,,,
C,03/13/2017,,1,2,
Looking at the above file structure, I want to loop through file and for each record(RECORD), I want to put next three lines in one array and then next record in separate array.
I am looking for skeleton code, I am very new in programming using stream readers.
Code so far
Dim read As New System.IO.StreamReader("C:\New Text.txt")
Dim a As String
For Each a In read.ReadLine
If read.ReadLine.Equals("RECORD") Then
\\How do I read next 3 lines and put them in one array with comma delimiter
End If
Next
You need a data structure where you will keep the lines loaded and then start your loop
' Here you will keep the 3 lines block joined together and splitted on comma
Dim records = new List(Of String())()
Using read As New System.IO.StreamReader("C:\New Text.txt")
Dim a As String
' Read each line (Assuming that we start with a RECORD line
For Each a In read.ReadLine
' Do not read another line, but just test what is present in the string
If a.Equals("RECORD") Then
' Now read the 3 following lines....
Dim line1 = read.ReadLine
Dim line2 = read.ReadLine
Dim line3 = read.ReadLine
'Join the lines togeter
Dim record = string.Join("", line1, line2, line3).
' Split on the comma to produce an array of strings. The fields.
Dim fields = record.Split(","c)
records.Add(fields)
End If
Next
End Using
Of course this should be enhanced using a proper class that describe your inputs where each property of the class represent a field of the CSV file loaded. And then you could change the List(Of String()) to a List(Of YourClassData)
Keep in mind that this solution is extremely dependant of an exact file structure. A line with the "RECORD" content should be always followed by three lines of data. (No blank lines allowed between the three data lines)
Dim read As New System.IO.StreamReader("C:\New Text.txt")
//I usually only do C#, but rough VB.NEt code: I did not rcreate your array for you, figured you got that :-)
Dim a As String
Dim myConcatString as String
For Each a In read.ReadLine
Dim myReadLine as String
myReadLine = read.ReadLine
If myReadLine.Equals("RECORD") Then
myConcatString = myConcatString & myReadLine
\\How do I read next 3 lines and put them in one array with comma delimiter
else
//add myConcatString to array if not null....
myConcatString =""
End If
Next

VB.net split function with substring

I want to read a certain value in a string. each line is a new string and I want to read the 6th integer on each line..
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles browsebtn.Click
If OpenFileDialog1.ShowDialog = Windows.Forms.DialogResult.OK Then
Dim filename As String = OpenFileDialog1.FileName
Dim streamreader As New System.IO.StreamReader(filename)
Dim textfile As String = streamreader.ReadToEnd
Dim splitChar As String = vbNewLine
Dim day As Integer = textfile.Substring(10, 2)
Dim strLine() As String = day.Split(splitChar)
For Each line As String In strLine
MsgBox(day)
Next
End If
End Sub
End Class
But it only returns one number. If I set day as a string and not an integer it works perfect, except it reads the whole string, not the two integers that I need. Please help. What am I doing wrong?
EDIT:
The input file looks like this:
23728 121010 00004986 00 00 2 21 22 11 447 114 2 382 292 350
23730 121010 00064120 00 00 51 19 21 12 1064 110 2 4500 572 7734
I want my output to be:
10
10
10 comes from "121010"
All of that code that you wrote could be done in much fewer lines, like this:
For Each line As String In File.ReadAllLines(fileName)
MessageBox.Show(line)
Next
Like your example, though, that loads the entire file into memory all at once, which could be problematic if it's a large file. If the size of the file is a concern, it would be better to just read one line at a time, like this:
Dim streamReader As New StreamReader(fileName)
Dim line As String = Nothing
Do
line = streamReader.ReadLine()
MessageBox.Show(line)
Loop Until line Is Nothing
However, the problem still remains, how do you split the line up into its individual values. If, as it appears in your question, the values are separated by spaces, then you can just use line.Split to separate the line into an array of all of its values. Then to get the last two characters of one of those values, you can just use String.SubString, like this:
Dim streamReader As New StreamReader(fileName)
Dim line As String = Nothing
Do
line = streamReader.ReadLine()
Dim parts() As String = line.Split()
Dim wholeNumber As String = parts(1)
Dim lastTwo As String = wholeNumber.SubString(wholeNumber.Length - 2)
MessageBox.Show(lastTwo)
Loop Until line Is Nothing
Some advice:
always dispose resources (use using or try catch finally with
resource.close)
never trust user inputs.
write codes that can handle enough undesired situations
Corrections based on your code:
Try
Dim text As String = Nothing
Using streamreader As New System.IO.StreamReader("text.txt")
text = streamreader.ReadToEnd()
End Using
If IsNothing(text) = False Then
Dim strLine() As String = text.Split(New String() {Environment.NewLine}, StringSplitOptions.None)
For Each line As String In strLine
If line.Length > 12 Then MsgBox(line.Substring(10, 2))
Next
End If
Catch ex As Exception
'filenotfound case
End Try
Another way:
On cases where line input can be different (but second should be looked value in our case)
Then can use Regex
Here is how:
Try
Using streamreader As New System.IO.StreamReader(file)
Dim line As String
While streamreader.Peek > 0
'unreaded line from file
line = streamreader.ReadLine()
'split input by non digits
Dim numberstrs As String() = Regex.Split(line, "\D+")
'second numbers last two
If numberstrs.Length > 1 AndAlso numberstrs(1).Length > 2 Then
Console.WriteLine("{0}", numberstrs(1).Substring(numberstrs(1).Length - 2, 2))
End If
End While
End Using
Catch ex As Exception
End Try
Steven's answer gets you most of the way there but not all the way. It's worth noting that you actually don't want the 6th integer because that could be 1 or 2 or pretty much anything depending on how you slice it. Also, given in your example you say you want to get 10 from 121010, that could be wither the second group of two numbers or the third group of two numbers from that section of the string.
I note that in your example strings you have some double spaces: You need to sort that out for a kickoff otherwise using String.Split will give you empty elements in the array. In fact, using parts(5) as Steven has used above gives you an empty element thanks to the double space, and that's not what you want anyway. You would want parts(2) and then you would need to SubString that to get the number you want.
Another, and I think more elegant, way to do it is to use a RegEx to get the number. Let's say you want the second 10 in that string (shown in bold): 12*10*10. If you know that that string will always be 6 characters, will always be the second field in the input line and you always want the third and fourth numbers then this would do you:
Imports System.Text.RegularExpressions
Imports System.IO
Private Sub ReadFile
Dim rx As New Regex("^[^\s]+\s{1}\d{2}(\d{2}?)", RegexOptions.Compiled Or RegexOptions.CultureInvariant)
Dim streamReader As New StreamReader(fileName)
Dim line As String = String.Empty
Do While streamReader.Peek >= 0
line = streamReader.ReadLine()
MessageBox.Show(rx.Matches(line)(0).Groups(1).Value)
Loop
End Sub
I'm not saying that's the only (or most elegant) RegEx but it will work and means you don't have to use SubString and it doesn't care how long the first field is. It also assumes a single space between fields but that can also be changed to suit. So long as you can work out a rule to get to the bit you want, you can RegEx it. Use Expresso (free and very powerful utility) to help you construct a suitable expression.

how to input data into an array from a text file that are vbTab separated?

I am having trouble turning a set of data from a .txt file into arrays, basically, what i have in the text file is:
Eddy vbtab 20
Andy vbtab 30
James vbtab 20
etc..
I want to set up the names as a Names array, and numbers as number array.
Now what I have done is
strFilename = "CustomerPrices.txt"
If File.Exists(strFilename) Then
Dim srReader As New StreamReader(strFilename)
intRecords = srReader.ReadLine()
intRows = intRecords
For i = 0 To intRows - 1
intLastBlank = strInput.IndexOf(vbTab)
strName(intPrices) = strInput.Substring(0, intLastBlank)
dblPrices(intPrices) = Double.Parse(strInput.Substring(intLastBlank + 1))
But when I debug I get a problem "Object Reference not set to an instance of an object"
Can anyone give me some advise?
Thanks
Separate arrays are probably a bad idea here. They group your data by fields, when it's almost always better to group your data by records. What you want instead is a single collection filled with classes of a particular type. Go for something like this:
Public Class CustomerPrice
Public Property Name As String
Public Property Price As Decimal
End Class
Public Function ReadCustomerPrices(ByVal fileName As String) As List(Of CustomerPrice)
Dim result As New List(Of CustomerPrice)()
Using srReader As New StreamReader(fileName)
Dim line As String
While (line = srReader.ReadLine()) <> Nothing
Dim data() As String = line.Split(vbTab)
result.Add(new CustomerPrice() From {Name = data(0), Price = Decimal.Parse(data(1))})
End While
End Using
Return result
End Function
Some other things worth noting in this code:
The Using block will guarantee the file is closed, even if an exception is thrown
It's almost never appropriate to check File.Exists(). It's wasteful code, because you still have to be able to handle the file io exceptions.
When working with money, you pretty much always want to use the Decimal type rather than Double
This code requires Visual Studio 2010 / .Net 4, and was typed directly into the reply window and so likely contains a bug, or even base syntax error.