Parallel Loop & Stringbuilder - vb.net

Intro: Hi all this is my first question, so please do let me know if I did or am doing anything wrong. I working on a project and one of its functions is to write a huge chunk of text to process the data and replace specific text at certain lines and output the file to a richtextbox/file.
Problem: The problem is that when I use a parallel foreach, my results in the richtextbox are all over the place, they are not according to lines (e.g. the first line may become the 15th line after the parallel foreach loop is run).
What the code does: It loops through a richtextbox and checks if the line matches the first line in a temporary richtextbox, if so it would stop the append and copy the text from the temporary richtextbox and let the loop run until the amount of lines which have past match the number of lines in the temporary richtextbox and then continue the append. It is basically a replace of text. Total output I am looking at about 20K lines.
Dim completertb4text As New StringBuilder("")
Parallel.ForEach(newrtb.Lines, Function() New StringBuilder(), Function(x, [option], sb)
If x.Contains(richtextboxl0) Then
startcount = True
sb.Append(vbNewLine & richtextbox3text & "111111")
End If
If startcount = True Then
If counter = temptextbox3count Then
startcount = False
Else
counter += 1
End If
End If
If sb.Length = 0 Then
sb.Append(vbNewLine & x & "222222")
End If
If sb.Length > 0 Then
sb.Append(vbNewLine & x & "3333333")
End If
Return sb
Function, Sub(sb)
SyncLock completertb4text
completertb4text.Append(sb.ToString())
End SyncLock
End Sub
Any help is kindly appreciated, thanks in advance!

Here is how I shrank my code from 08:00 seconds to 00:01 seconds.
Dim counter As Integer = 0
Dim countertrue As Integer = 0
Dim countintertal As Integer = 0
Dim newrtbstrarray As String() = newrtb.Lines
Dim rtb3array As String() = richtextbox3text.Lines
For Each line As String In newrtbstrarray
If line.Contains(richtextboxl0) Then
countertrue = counter
For Each element As String In rtb3array
newrtbstrarray(countertrue) = rtb3array(countintertal)
countertrue += 1
countintertal += 1
Next
End If
counter += 1
Next

Related

Splitting string every 100 characters not working

I am having a problem where I just can't seem to get it to split or even display the message. The message variable is predefined in another part of my code and I have debugged to make sure that the value comes through. I am trying to get it so that every 100 characters it goes onto a new line and with every message it also goes onto a new line.
y = y - 13
messagearray.AddRange(Message.Split(ChrW(100)))
Dim k = messagearray.Count - 1
Dim messagefin As String
messagefin = ""
While k > -1
messagefin = messagefin + vbCrLf + messagearray(k)
k = k - 1
End While
k = 0
Label1.Text = Label1.Text & vbCrLf & messagefin
Label1.Location = New Point(5, 398 + y)
You can use regular expression. It will create the array of strings where every string contains 100 characters. If the amount of remained characters is less than 100, it will match all of them.
Dim input = New String("A", 310)
Dim mc = Regex.Matches(input, ".{1,100}")
For Each m As Match In mc
'// Do something
MsgBox(m.Value)
Next
You can use LINQ to do that.
When you do a Select you can get the index of the item by including a second parameter. Then group the characters by that index divided by the line length so, the first character has index 0, and 0 \ 100 = 0, all the way up to the hundredth char which has index 99: 99 \ 100 = 0. The next hundred chars have 100 \ 100 = 1 to 199 \ 100 = 1, and so on (\ is the integer division operator in VB.NET).
Dim message = New String("A"c, 100)
message &= New String("B"c, 100)
message &= New String("C"c, 99)
Dim lineLength = 100
Dim q = message.Select(Function(c, i) New With {.Char = c, .Idx = i}).
GroupBy(Function(a) a.Idx \ lineLength).
Select(Function(b) String.Join("", b.Select(Function(d) d.Char)))
TextBox1.AppendText(vbCrLf & String.Join(vbCrLf, q))
It is easy to see how to change the line length because it is in a variable with a meaningful name, for example I set it to 50 to get the output
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
You can use String.SubString to do that. Like this
Dim Message As String = "your message here"
Dim MessageList As New List (Of String)
For i As Integer = 0 To Message.Length Step 100
If (Message.Length < i + 100) Then
MessageList.Add(Message.SubString (i, Message.Length - i)
Exit For
Else
MessageList.Add(Message.SubString (i, 100))
End If
Next
Dim k = MessageList.Count - 1
...
Here is what your code produced with a bit of clean up. I ignored the new position of the label.
Private Sub OpCode()
Dim messagearray As New List(Of String) 'I guessed that messagearray was a List(Of T)
messagearray.AddRange(Message.Split(ChrW(100))) 'ChrW(100) is lowercase d
Dim k = messagearray.Count - 1
Dim messagefin As String
messagefin = ""
While k > -1
messagefin = messagefin + vbCrLf + messagearray(k)
k = k - 1
End While
k = 0 'Why reset k? It falls out of scope at End Sub
Label1.Text = Label1.Text & vbCrLf & messagefin
End Sub
I am not sure why you think that splitting a string by lowercase d would have anything to do with getting 100 characters. As you can see the code reversed the order of the list items. It also added a blank line between the existing text in the label (In this case Label1) and the new text.
To accomplish your goal, I first created a List(Of String) to store the chunks. The For loop starts at the beginning of the input string and keeps going to the end increasing by 10 on each iteration.
To avoid an index out of range which would happen at the end. Say, we only had 6 characters left from start index. If we tried to retrieve 10 characters we would have an index out of range.
At the end we join the elements of the string with the separated of new line.
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
BreakInto10CharacterChunks("The quick brown fox jumped over the lazy dogs.")
End Sub
Private Sub BreakInto10CharacterChunks(input As String)
Dim output As New List(Of String)
Dim chunk As String
For StartIndex = 0 To input.Length Step 10
If StartIndex + 10 > input.Length Then
chunk = input.Substring(StartIndex, input.Length - StartIndex)
Else
chunk = input.Substring(StartIndex, 10)
End If
output.Add(chunk)
Next
Label1.Text &= vbCrLf & String.Join(vbCrLf, output)
End Sub
Be sure to look up String.SubString and String.Join to fully understand how these methods work.
https://learn.microsoft.com/en-us/dotnet/api/system.string.substring?view=netframework-4.8
and https://learn.microsoft.com/en-us/dotnet/api/system.string.join?view=netframework-4.8

VB "Index was out of range, must be non-negative and less than the size of the collection." When trying to generate a random number more than once

So I'm trying to generate a random number on button click. Now this number needs to be between two numbers that are inside my text file with various other things all separated by the "|" symbol. The number is then put into the text of a textbox which is being created after i run the form. I can get everything to work perfectly once, but as soon as i try to generate a different random number it gives me the error: "Index was out of range, must be non-negative and less than the size of the collection." Here is the main code as well as the block that generates the textbox after loading the form. As well as the contents of my text file.
Private Sub generate()
Dim newrandom As New Random
Try
Using sr As New StreamReader(itemfile) 'Create a stream reader object for the file
'While we have lines to read in
Do Until sr.EndOfStream
Dim line As String
line = sr.ReadLine() 'Read a line out one at a time
Dim tmp()
tmp = Split(line, "|")
rows(lineNum).buybutton.Text = tmp(1)
rows(lineNum).buyprice.Text = newrandom.Next(tmp(2), tmp(3)) 'Generate the random number between two values
rows(lineNum).amount.Text = tmp(4)
rows(lineNum).sellprice.Text = tmp(5)
rows(lineNum).sellbutton.Text = tmp(1)
lineNum += 1
If sr.EndOfStream = True Then
sr.Close()
End If
Loop
End Using
Catch x As Exception ' Report any errors in reading the line of code
Dim errMsg As String = "Problems: " & x.Message
MsgBox(errMsg)
End Try
End Sub
Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
rows = New List(Of duplicate)
For dupnum = 0 To 11
'There are about 5 more of these above this one but they all have set values, this is the only troublesome one
Dim buyprice As System.Windows.Forms.TextBox
buyprice = New System.Windows.Forms.TextBox
buyprice.Width = textbox1.Width
buyprice.Height = textbox1.Height
buyprice.Left = textbox1.Left
buyprice.Top = textbox1.Top + 30 * dupnum
buyprice.Name = "buypricetxt" + Str(dupnum)
Me.Controls.Add(buyprice)
pair = New itemrow
pair.sellbutton = sellbutton
pair.amount = amounttxt
pair.sellprice = sellpricetxt
pair.buybutton = buybutton
pair.buyprice = buypricetxt
rows.Add(pair)
next
end sub
'textfile contents
0|Iron Sword|10|30|0|0
1|Steel Sword|20|40|0|0
2|Iron Shield|15|35|0|0
3|Steel Shield|30|50|0|0
4|Bread|5|10|0|0
5|Cloak|15|30|0|0
6|Tent|40|80|0|0
7|Leather Armour|50|70|0|0
8|Horse|100|200|0|0
9|Saddle|50|75|0|0
10|Opium|200|500|0|0
11|House|1000|5000|0|0
Not sure what else to add, if you know whats wrong please help :/ thanks
Add the following two lines to the start of generate():
Private Sub generate()
Dim lineNum
lineNum = 0
This ensures that you don't point to a value of lineNum outside of the collection.
I usually consider it a good idea to add
Option Explicit
to my code - it forces me to declare my variables, and then I think about their initialization more carefully. It helps me consider their scope, too.
Try this little modification.
I took your original Sub and changed a little bit take a try and let us know if it solve the issue
Private Sub generate()
Dim line As String
Dim lineNum As Integer = 0
Dim rn As New Random(Now.Millisecond)
Try
Using sr As New StreamReader(_path) 'Create a stream reader object for the file
'While we have lines to read in
While sr.Peek > 0
line = sr.ReadLine() 'Read a line out one at a time
If Not String.IsNullOrEmpty(line) And Not String.IsNullOrWhiteSpace(line) Then
Dim tmp()
tmp = Split(line, "|")
rows(lineNum).buybutton.Text = tmp(1)
rows(lineNum).buyprice.Text = rn.Next(CInt(tmp(2)), CInt(tmp(3))) 'Generate the random number between two values
rows(lineNum).amount.Text = tmp(4)
rows(lineNum).sellprice.Text = tmp(5)
rows(lineNum).sellbutton.Text = tmp(1)
lineNum += 1
End If
End While
End Using
Catch x As Exception ' Report any errors in reading the line of code
Dim errMsg As String = "Problems: " & x.Message
MsgBox(errMsg)
End Try
End Sub

VB Reading a text file into a 2D array?

I am trying to create a sudoku game, with load and save game functions in VB and I was wondering how it would be possible to load a save file(the numbers on the sudoku board and time score) and make the numbers within the file correspond to their exact location on the new board. For saving the file I have this:
Private Sub saveBoard(fileName As String)
Dim f As StreamWriter = New StreamWriter(fileName)
For col = 0 To 8
Dim sudokuLine = ""
For row = 0 To 8
If (board(row, col).Text = "") Then
sudokuLine += "0"
Else : sudokuLine += board(row, col).Text
End If
sudokuLine += " "
Next
f.WriteLine(sudokuLine)
Next
f.WriteLine(lblMinutes.Text + " " + lblSeconds.Text)
f.Close()
End Sub
Also I know about StreamReader...
This could be the loadBoard that corresponds to your saveBoard.
Of course a bit of testing is required here and a more fool proof approach to handle critical errors (like a different file passed as input) .
Notice that I have added the using statement around the opening of the Stream. This should be done also in the saveBoard above to avoid problems with files locked in case of exceptions.
Private Sub loadBoard(fileName As String)
Using f = New StreamReader(fileName)
For col = 0 To 8
Dim sudokuColumn = f.ReadLine()
Dim cells() = sudokuColum.Split()
For row = 0 To 8
If cells(row, col) = "0") Then
boards(row, col).Text = ""
Else
board(row, col).Text = cells(row,col)
End If
Next
Next
lblMinutes.Text = f.ReadLine()
End Using
End Sub

What is causing 'Index was outside the bounds of the array' error?

What is causing 'Index was outside the bounds of the array' error? It can't be my file, defianetly not. Below is my code:
Sub pupiltest()
Dim exitt As String = Console.ReadLine
Do
If IsNumeric(exitt) Then
Exit Do
Else
'error message
End If
Loop
Select Case exitt
Case 1
Case 2
Case 3
End Select
Do
If exitt = 1 Then
pupilmenu()
ElseIf exitt = 3 Then
Exit Do
End If
Loop
Dim score As Integer
Dim word As String
Dim totalscore As Integer = 0
'If DatePart(DateInterval.Weekday, Today) = 5 Then
'Else
' Console.WriteLine("You are only allowed to take the test on Friday unless you missed it")
' pupiltest()
'End If
Dim founditem() As String = Nothing
For Each line As String In File.ReadAllLines("F:\Computing\Spelling Bee\stdnt&staffdtls.csv")
Dim item() As String = line.Split(","c)
founditem = item
Next
Dim stdntfname As String = founditem(3)
Dim stdntsname As String = founditem(4)
Dim stdntyear As String = founditem(5)
Console.Clear()
If founditem IsNot Nothing Then
Do
If stdntyear = founditem(5) And daytoday = founditem(6) Then
Exit Do
ElseIf daytoday <> founditem(6) Then
Console.WriteLine("Sorry you are not allowed to do this test today. Test available on " & item(6).Substring(0, 3) & "/" & item(6).Substring(3, 6) & "/" & item(6).Substring(6, 9))
Threading.Thread.Sleep(2500)
pupiltest()
ElseIf stdntyear <> founditem(5) Then
Console.WriteLine("Year not found, please contact the system analysts")
Threading.Thread.Sleep(2500)
pupiltest()
End If
Loop
End If
For Each line As String In File.ReadAllLines("F:\Computing\Spelling Bee\testtests.csv")
Dim item() As String = line.Split(","c)
Dim mine As String = String.Join(",", item(2), item(3), item(4), item(5), item(6))
For i As Integer = 1 To 10
Console.WriteLine(i.ToString & "." & item(1))
Console.Write("Please enter the word: ")
word = Console.ReadLine
If word = Nothing Or word <> item(0) Then
score += 0
ElseIf word = item(0) Then
score += 2
ElseIf word = mine Then
score += 1
End If
Next
If score > 15 Then
Console.WriteLine("Well done! Your score is" & score & "/20")
ElseIf score > 10 Then
Console.WriteLine("Your score is" & score & "/20")
ElseIf score Then
End If
Next
Using sw As New StreamWriter("F:\Computing\Spelling Bee\stdntscores", True)
sw.Write(stdntfname, stdntsname, stdntyear, score, daytoday, item(7))
Try
Catch ex As Exception
MsgBox("Error accessing designated file")
End Try
End Using
End
End Sub
All help is highly appreciated,
You are constantly replacing the foundItem array when you do founditem = item:
Dim founditem() As String = Nothing
For Each line As String In File.ReadAllLines("F:\Computing\Spelling Bee\stdnt&staffdtls.csv")
Dim item() As String = line.Split(","c)
founditem = item
Next
Also, you are using (=) the assignment operation instead of (==) relational operator, to compare. Refer to this article for help in understanding the difference between the two.
Instead of this: If stdntyear = founditem(5) And daytoday = founditem(6) Then
Use this: If (stdntyear == founditem(5)) And (daytoday == founditem(6)) Then
Now back to your main error. You continue to assign the itemarray to founditem every time you iterate (Which overwrites previous content). At the end of the Iteration you will be left with the last entry in your CSV only... So in other words, founditem will only have 1 element inside of it. If you try to pick out ANYTHING but index 0, it will throw the exception index was outside the bounds of the array
So when you try to do the following later, it throws the exception.
Dim stdntfname As String = founditem(3) 'index 3 does not exist!
To fix it do the following change:
Dim founditem() As String = Nothing
For Each line As String In File.ReadAllLines("F:\Computing\Spelling Bee\stdnt&staffdtls.csv")
'NOTE: Make sure you know exactly how many columns your csv has or whatever column
' you wish to access.
Dim item() As String = line.Split(","c)
founditem(0) = item(0) 'Assign item index 0 to index 0 of founditem...
founditem(1) = item(1)
founditem(2) = item(2)
founditem(3) = item(3)
founditem(4) = item(4)
founditem(5) = item(5)
founditem(6) = item(6)
Next
For more help on how to work with VB.NET Arrays visit this site: http://www.dotnetperls.com/array-vbnet
In your line Dim item() As String = line.Split(","c) there's no guarantee that the correct number of elements exist. It's possible that one of the lines is missing a comma or is a blank trailing line in the document. You might want to add a If item.Length >= 7 and skipping rows that don't have the right number of rows. Also, remember that unlike VB6, arrays in .Net are 0 based not 1 based so make sure that item(6) is the value that you think it is.

random number in multi threading not working

please examine my code below :
Public Class tier1
Dim rnd As New System.Random()
Function build1(ByVal dt As DataTable) As String
Try
For i = 0 To 4
For ix As Integer = 0 To till Step 4
lstrn.Add(rnd.Next(ix, ix + 4))
Next
Dim cntx As Integer = 0
For Each x As Integer In lstrn
If (i = 0) Then
If (article(x).Split(ChrW(10)).Length > 2) Then
If (article(x).Split(ChrW(10))(0).Length > 300) Then
first.Add(article(x).Split(ChrW(10))(0))
cntx = cntx + 1
If (cntx = 25) Then
Exit For
End If
End If
End If
End If
lstrn.Clear()
Next
Dim fi as String = "{"
For dx As Integer = 0 To first.Count - 2
fi = fi & w.spinl(first(dx), "light") & "|"
Next
fi = fi & "}"
Return fi
Catch ex As Exception
End Try
End Function
End Class
Now see my calling code :
Dim w As WaitCallback = New WaitCallback(AddressOf beginscn)
For var As Integer = 1 To NumericUpDown1.Value
Dim param(1) As Object
param(0) = lst
param(1) = var
ThreadPool.QueueUserWorkItem(w, param)
Next
sub
sub beginscn()
Dim scntxt As String = t1.buildtier1(dt)
end sub
Now understand what i give and what i want. Suppose i pass a datatable like this :
1,abcd,34,5
2,adfg,34,5
3,fhjrt,34,5
4,rtitk,34,5
What i want is {abcd|adfg|fhjrt|rtitk} and this sequence should be random everytime. Since i pass like 50-100 values and exit loop at 25 each output should have a different sequence of 25 strings in {|} format but it does not work like that. Everytime i get same sequence.
Can anyone explain why does it do like that and any possible solution for this problem?
Note : I have already tried shuffling datatable just before queuing it but still it does not work.
The random object is not thread safe. You could work around this by creating separate instances of the random object in each thread and use the thread ID to generate the
seed.
http://msdn.microsoft.com/en-us/library/ms683183%28VS.85%29.aspx