Clearing a text file in Visual basic - vb.net

This is the code i have so far, When a user gets a new high score it needs to clear the txt file and put the new high score in it or replace the number within the txt file. I am struggling to find a way to clear the file.
ElseIf HighscoreDifficulty = "E" Then
EasyHighScore = My.Computer.FileSystem.ReadAllText("EasyHighScore.txt")
If CurrentScore > EasyHighScore Then
NewHighScore.Visible = True
file = My.Computer.FileSystem.OpenTextFileWriter("EasyHighScore.txt", True)
file.WriteLine(CurrentScore)
file.Close()
Else
NoNewHighScore.Visible = True
End If
Thanks

Let's say that you want to keep the top five scores in the file. Assuming that the file always contains valid data, you could do that like this:
Private Sub SaveHighScore(score As Integer)
Const FILE_PATH = "file path here"
Const MAX_SCORE_COUNT = 5
'Read the lines of the file into a list of Integer values.
Dim scores = File.ReadLines(FILE_PATH).
Select(Function(s) CInt(s)).
ToList()
'Append the new score.
scores.Add(score)
'Sort the list in descending order.
scores.Sort(Function(x, y) y.CompareTo(x))
'Write up to the first five scores back tot he file.
File.WriteAllLines(FILE_PATH,
scores.Take(MAX_SCORE_COUNT).
Select(Function(i) i.ToString()))
End Sub
By adding the new score to the existing list, sorting and then writing out the first five, you automatically drop the lowest score. That means that there's never a need to actually check whether the new score is a high score or not.

Related

Random images on buttons

Good evening made the following code, create an array of buttons and panels, now how do I insert random images to those buttons, can you help me please.
Public Sub crearBotonesPaneles(ByVal creaBoton(,) As Button, ByVal creaPanel As Panel)
Dim puntoLocacion As Point
puntoLocacion.X = 20
puntoLocacion.Y = 40
For filas As Integer = 0 To 1
For columnas As Integer = 0 To 3
If IsNothing(creaBoton(filas, columnas)) Then
creaBoton(filas, columnas) = New Button
creaBoton(filas, columnas).Location = puntoLocacion
creaBoton(filas, columnas).Width = 50
creaBoton(filas, columnas).Height = 50
creaPanel.Controls.Add(creaBoton(filas, columnas))
puntoLocacion.X = puntoLocacion.X + 50
End If
Next
puntoLocacion.X = 20
puntoLocacion.Y = puntoLocacion.Y + 50
Next
End Sub
Doing something random ALWAYS means generating one or more random numbers in an appropriate range and then using them in an appropriate manner. The way you determine the range is application-specific and the manner in which you use the number(s) is application-specific. In your case, you might get the paths of all the image files in a folder, use random numbers to order those paths randomly and then use the paths one by one to get the images from the files. E.g.
Private rng As New Random 'Random number generator
Private imagePaths As Queue(Of String)
Private Sub LoadImagePaths()
'Create a new queue of file paths sorted based on a random number mapped to each one.
imagePaths = New Queue(Of String)(Directory.EnumerateFiles(My.Computer.FileSystem.SpecialDirectories.MyPictures,
"*.jpg").
OrderBy(Function(s) rng.NextDouble()))
End Sub
Private Function GetNextImage() As Image
'Load the image paths is there is no queue or the current queue is empty.
If imagePaths?.Any() = False Then
LoadImagePaths()
End If
'Create an image from the next file in the queue.
Return Image.FromFile(imagePaths.Dequeue())
End Function
Based on that code, you just call GetNextImage each time you need a random image. There will be no repeats until the entire list is exhausted.

Highlight DataGridViewRows based on value comparison with other rows

I have a Part class with the fields list in the code below. I have a DataGridView control, which I am filtering with the Advanced DGV (ADGV) DLL from NUGET. I must include the ADGV in my winform. I currently have a DataGridView, a search box on the form, and a button to run the following function. I need to go through all of the visible rows, collect a unique list of part numbers with their most recent revisions, and then color the rows in DataGridView which are out of date by checking the part number and rev on each row against the mostuptodate list. For 45,000 entries displayed in DataGridView, this take ~17 secs. For ~50 entries, it take ~1.2 seconds. This is extremely inefficient, but I can't see a way to cut the time down.
Sub highlightOutdatedParts()
'Purpose: use the results in the datagridview control, find the most recent revision of each part, and
' highlight all outdated parts relative to their respective most recent revisions
'SORT BY PART NUMBER AND THEN BY REV
If resultsGrid.ColumnCount = 0 Or resultsGrid.RowCount = 0 Then Exit Sub
Dim stopwatch As New Stopwatch
stopwatch.Start()
resultsGrid.Sort(resultsGrid.Columns("PartNumber"), ListSortDirection.Ascending)
Dim iBag As New ConcurrentBag(Of Part)
Dim sortedList As Generic.List(Of Part)
For Each row As DataGridViewRow In resultsGrid.Rows
If row.Visible = True Then
Dim iPart As New Part()
Try
iPart.Row = row.Cells(0).Value
iPart.Workbook = CStr(row.Cells(1).Value)
iPart.Worksheet = CStr(row.Cells(2).Value)
iPart.Product = CStr(row.Cells(3).Value)
iPart.PartNumber = CStr(row.Cells(4).Value)
iPart.ItemNo = CStr(row.Cells(5).Value)
iPart.Rev = CStr(row.Cells(6).Value)
iPart.Description = CStr(row.Cells(7).Value)
iPart.Units = CStr(row.Cells(8).Value)
iPart.Type = CStr(row.Cells(9).Value)
iPart.PurchCtgy = CStr(row.Cells(10).Value)
iPart.Qty = CDbl(row.Cells(11).Value)
iPart.TtlPerProd = CDbl(row.Cells(12).Value)
iPart.Hierarchy = CStr(row.Cells(13).Value)
iBag.Add(iPart)
Catch ice As InvalidCastException
Catch nre As NullReferenceException
End Try
End If
Next
sortedList = (From c In iBag Order By c.PartNumber, c.Rev).ToList() ' sort and convert to list
Dim mostUTDRevList As New Generic.List(Of Part) ' list of most up to date parts, by Rev letter
For sl As Integer = sortedList.Count - 1 To 0 Step -1 'start at end of list and work to beginning
Dim query = From entry In mostUTDRevList ' check if part number already exists in most up to date list
Where entry.PartNumber = sortedList(sl).PartNumber
Select entry
If query.Count = 0 Then ' if this part does not already exist in the list, add.
mostUTDRevList.Add(sortedList(sl))
End If
Next
'HIGHLIGHT DATAGRIDVIEW ROWS WHERE PART NUMBERS ARE OUT OF DATE
For Each row As DataGridViewRow In resultsGrid.Rows
' if that part with that Rev does not exist in the list, it must be out of date
Try
Dim rowPN As String = CStr(row.Cells(4).Value).ToUpper ' get part number
Dim rowR As String = CStr(row.Cells(6).Value).ToUpper ' get Rev
Dim query = From entry In mostUTDRevList ' check if that part number with that Rev is in the list.
Where entry.PartNumber.ToUpper.Equals(rowPN) AndAlso
entry.Rev.ToUpper.Equals(rowR)
Select entry
If query.Count = 0 Then ' if the part is out of date highlight its' row
row.DefaultCellStyle.BackColor = Color.Chocolate
End If
Catch ex As NullReferenceException
Catch ice As InvalidCastException
End Try
Next
resultsGrid.Select()
stopwatch.Stop()
If Not BackgroundWorker1.IsBusy() Then timertextbox.Text = stopwatch.Elapsed.TotalSeconds.ToString & " secs"
MessageBox.Show("Highlighting completed successfully.")
End Sub
It is almost always faster to work with the data than the control. The control is simply the means to present a view of the data (in a grid) to the users. Working with the data from there requires too much converting to be effieicent. Then, use the DGV events to highlight the rows
Its hard to tell all the details of what you are doing, but it looks like you are comparing the data to itself (as opposed to some concrete table where the lastest revision codes are defined). Nor is it clear why the datasources are collections, ConcurrentBags etc. The key would be to use collections optimized for the job.
To demonstrate, I have a table with 75,000 rows; the product codes are randomly selected from a pool of 25,000 and a revision code is a random integer (1-9). After the DGV datasource is built (a DataTable) a LookUp is created from the ProductCode-Revision pair. This is done once and once only:
' form level declaration
Private PRCodes As ILookup(Of String, Int32)
' go thru table
' group by the product code
' create an anon Name-Value object for each,
' storing the code and highest rev number
' convert result to a LookUp
PRCodes = dtSample.AsEnumerable.
GroupBy(Function(g) g.Item("ProductCode"),
Function(key, values) New With {.Name = key.ToString(), .Value = values.
Max(Of Int32)(Function(j) j.Field(Of Int32)("RevCode"))
}).
ToLookup(Of String, Int32)(Function(k) k.Name, Function(v) v.Value)
Elapsed time via stopwatch: 81 milliseconds to create the collection of 23731 items. The code uses an anonymous type to store a Max Revision code for each product code. A concrete class could also be used. If you're worried about mixed casing, use .ToLowerInvariant() when creating the LookUp (not ToUpper -- see What's Wrong With Turkey?) and again later when looking up the max rev.
Then rather than looping thru the DGV rows use the RowPrePaint event:
If e.RowIndex = -1 Then Return
If dgv1.Rows(e.RowIndex).IsNewRow Then Return
' .ToLowerInvariant() if the casing can vary row to row
Dim pc = dgv1.Rows(e.RowIndex).Cells("ProductCode").Value.ToString()
Dim rv = Convert.ToInt32(dgv1.Rows(e.RowIndex).Cells("RevCode").Value)
Dim item = PRCodes(pc)(0)
If item > rv Then
dgv1.Rows(e.RowIndex).DefaultCellStyle.BackColor = Color.MistyRose
End If
Notes
It takes some time to create the DataSource, but 75,000 rows is a lot to throw at a user
The time to create the LookUp is minimal - barely measurable
There is no noticeable wait in displaying them because a) the LookUp is made for this sort of thing, b) rows are done as needed when they are displayed. Row # 19,999 may never be processed if the user never scrolls that far.
This is all geared to just color a row. If you needed to save the Current/NotCurrent state for each row, add a Boolean column to the DataTable and loop on that. The column can be invisible if to hide it from the user.
The random data results in 47,000 out of date RevCodes. Processing 75k rows in the DataTable to set the flag takes 591 milliseconds. You would want to do this before you set the DataTable as the DataSource to prevent changes to the data resulting in various events in the control.
In general, the time to harvest the max RevCode flag and even tag the out of date rows is a trivial increment to creating the datasource.
The Result:
The data view is sorted by ProductCode so that the coloring of lower RevCodes is apparent.
We surely cant grok all the details and constraints of the system from a small snippet - even the data types and original datasource are a guess for us. However, this should provide some help with better look-up methods, and the concept of working with the data rather than the user's view.
One thing is the revision code - yours is treating them as a string. If this is alphanumeric, it may well not compare correctly - "9" sorts/compares higher than "834" or "1JW".
See also:
Lookup(Of TKey, TElement) Class
Anonymous Types
The solution was spurred in part by #Plutonix.
Sub highlightOutdatedParts()
If resultsGrid.ColumnCount = 0 Or resultsGrid.RowCount = 0 Then Exit Sub
Dim stopwatch As New Stopwatch
stopwatch.Start()
resultsGrid.DataSource.DefaultView.Sort = "PartNumber ASC, Rev DESC"
resultsGrid.Update()
'HIGHLIGHT DATAGRIDVIEW ROWS WHERE PART NUMBERS ARE OUT OF DATE
Dim irow As Long = 0
Do While irow <= resultsGrid.RowCount - 2
' if that part with that Rev does not exist in the list, it must be out of date
Dim utdPN As String = resultsGrid.Rows(irow).Cells(4).Value.ToString().ToUpper()
Dim utdRev As String = resultsGrid.Rows(irow).Cells(6).Value.ToString().ToUpper()
Dim iirow As Long = irow + 1
'If iirow > resultsGrid.RowCount - 1 Then Exit Do
Dim activePN As String = Nothing
Dim activeRev As String = Nothing
Try
activePN = resultsGrid.Rows(iirow).Cells(4).Value.ToString().ToUpper()
activeRev = resultsGrid.Rows(iirow).Cells(6).Value.ToString().ToUpper()
Catch ex As NullReferenceException
End Try
Do While activePN = utdPN
If iirow > resultsGrid.RowCount - 1 Then Exit Do
If activeRev <> utdRev Then
resultsGrid.Rows(iirow).DefaultCellStyle.BackColor = Color.Chocolate
End If
iirow += 1
Try
activePN = resultsGrid.Rows(iirow).Cells(4).Value.ToString().ToUpper()
activeRev = resultsGrid.Rows(iirow).Cells(6).Value.ToString().ToUpper()
Catch nre As NullReferenceException
Catch aoore As ArgumentOutOfRangeException
End Try
Loop
irow = iirow
Loop
resultsGrid.Select()
stopwatch.Stop()
If Not BackgroundWorker1.IsBusy() Then
timertextbox.Text = stopwatch.Elapsed.TotalSeconds.ToString & " secs"
resultcounttextbox.Text = resultsGrid.RowCount - 1 & " results"
End If
MessageBox.Show("Highlighting completed successfully.")
End Sub

How can i check for a character after certain text within a listbox?

How can i check for a character after other text within a listbox?
e.g
Listbox contents:
Key1: V
Key2: F
Key3: S
Key4: H
How do I find what comes after Key1-4:?
Key1-4 will always be the same however what comes after that will be user defined.
I figured out how to save checkboxes as theres only 2 values to choose from, although user defined textboxes is what im struggling with. (I have searched for solutions but none seemed to work for me)
Usage:
Form1_Load
If ListBox1.Items.Contains("Key1: " & UsersKey) Then
TextBox1.Text = UsersKey
End If
Which textbox1.text would then contain V / whatever the user defined.
I did try something that kind of worked:
Form1_Load
Dim UsersKey as string = "V"
If ListBox1.Items.Contains("Key1: " & UsersKey) Then
TextBox1.Text = UsersKey
End If
but i'm not sure how to add additional letters / numbers to "V", then output that specific number/letter to the textbox. (I have special characters blocked)
Reasoning I need this is because I have created a custom save settings which saves on exit and loads with form1 as the built in save settings doesn't have much customization.
e.g Can't choose save path, when filename is changed a new user.config is generated along with old settings lost.
Look at regular expressions for this.
Using the keys from your sample:
Dim keys As String = "VFSH"
Dim exp As New RegEx("Key[1-4]: ([" & keys& "])")
For Each item As String in ListBox1.Items
Dim result = exp.Match(item)
If result.Success Then
TextBox1.Text = result.Groups(1).Value
End If
Next
It's not clear to me how your ListBoxes work. If you might find, for example, "Key 2:" inside ListBox1 that you need to ignore, you will want to change the [1-4] part of the expression to be more specific.
Additionally, if you're just trying to exclude unicode or punctuation, you could also go with ranges:
Dim keys As String = "A-Za-z0-9"
If you are supporting a broader set of characters, there are some you must be careful with: ], \, ^, and - can all have special meanings inside of a regular expression character class.
You have multiple keys, I assume you have multiple textboxes to display the results?
Then something like this would work. Loop thru the total number of keys, inside that you loop thru the alphabet. When you find a match, output to the correct textbox:
Dim UsersKey As String
For i As Integer = 1 To 4
For Each c In "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray()
UsersKey = c
If ListBox1.Items.Contains("Key" & i & ": " & UsersKey) Then
Select Case i
Case 1
TextBox1.Text = UsersKey
Case 2
TextBox2.Text = UsersKey
Case 3
TextBox3.Text = UsersKey
Case 4
TextBox4.Text = UsersKey
End Select
Exit For 'match found so exit inner loop
End If
Next
Next
Also, you say your settings are lost when the filename is changed. I assume when the version changes? The Settings has an upgrade method to read from a previous version. If you add an UpgradeSettings boolean option and set it to True and then do this at the start of your app, it will load the settings from a previous version:
If My.Settings.UpgradeSettings = True Then
My.Settings.Upgrade()
My.Settings.Reload()
My.Settings.UpgradeSettings = False
My.Settings.Save()
End If
Updated Answer:
Instead of using a listtbox, read the settings file line by line and output the results to the correct textbox based on the key...something like this:
Dim settingsFile As String = "C:\settings.txt"
If IO.File.Exists(settingsFile) Then
For Each line As String In IO.File.ReadLines(settingsFile)
Dim params() As String = Split(line, ":")
If params.Length = 2 Then
params(0) = params(0).Trim
params(1) = params(1).Trim
Select Case params(0)
Case "Key1"
Textbox1.Text = params(1)
Case "Key2"
Textbox2.Text = params(1)
End Select
End If
Next line
End If
You can associate text box with a key via its Name or Tag property. Lets say you use Name. In this case TextBox2 is associated with key2. TextBox[N] <-> Key[N]
Using this principle the code will look like this [considering that your list item is string]
Sub Test()
If ListBox1.SelectedIndex = -1 Then Return
Dim data[] As String = DirectCast(ListBox1.SelectedItem, string).Split(new char(){":"})
Dim key As String = data(0).Substring(3)
Dim val As String = data(1).Trim()
' you can use one of the known techniques to get control on which your texbox sits.
' I omit this step and assume "Surface1" being a control on which your text boxes sit
DirectCast(
(From ctrl In Surface1.Controls
Where ctrl.Name = "TextBox" & key
Select ctrl).First()), TextBox).Text = val
End Sub
As you can see, using principle I just explained, you have little parsing and what is important, there is no growing Select case if, lets say, you get 20 text boxes. You can add as many text boxes and as many corresponding list items as you wish, the code need not change.

How to read data from text file and split in other textboxes?

I can read all text from a text file(*.txt) using readalltext function in VB, but I want to split the text in other textboxes and one text file contains data and I want to split it, my text file is next:
x=first name
y=last name
z=age
And I want the code which can manage read data from that text file and after that split data in three textboxes like that.
textbox1.text=x
textbox2.text=y
textbox3.text=z
and last output was that
textbox1.text=first name
textbox2.text=last name
textbox3.text=age
So my problem is next: I want the code that can read x, y and z values from one text file.
Here's an example of code :
For Each line In File.ReadAllLines(file)
Select Case True
Case line.StartsWith("x=")
TextBox1.Text = line.Split("=")(1)
Case line.StartsWith("y=")
TextBox2.Text = line.Split("=")(1)
Case line.StartsWith("z=")
TextBox3.Text = line.Split("=")(1)
End Select
Next
Consider loading the contents into a List(Of String()):
Dim lst = File.ReadAllLines("filename.txt").Select(Function(line) line.Split("=")).ToList
Then if you store your textboxes in a List(Of TextBox), you could fill the corresponding textboxes appropriately:
For i = 0 To lst.Count
textboxes(i).Text = lst(i)(1)
Next

Start reading massive text file from the end

I would ask if you could give me some alternatives in my problems.
basically I'm reading a .txt log file averaging to 8 million lines. Around 600megs of pure raw txt file.
I'm currently using streamreader to do 2 passes on those 8 million lines doing sorting and filtering important parts in the log file, but to do so, My computer is taking ~50sec to do 1 complete run.
One way that I can optimize this is to make the first pass to start reading at the end because the most important data is located approximately at the final 200k line(s) . Unfortunately, I searched and streamreader can't do this. Any ideas to do this?
Some general restriction
# of lines varies
size of file varies
location of important data varies but approx at the final 200k line
Here's the loop code for the first pass of the log file just to give you an idea
Do Until sr.EndOfStream = True 'Read whole File
Dim streambuff As String = sr.ReadLine 'Array to Store CombatLogNames
Dim CombatLogNames() As String
Dim searcher As String
If streambuff.Contains("CombatLogNames flags:0x1") Then 'Keyword to Filter CombatLogNames Packets in the .txt
Dim check As String = streambuff 'Duplicate of the Line being read
Dim index1 As Char = check.Substring(check.IndexOf("(") + 1) '
Dim index2 As Char = check.Substring(check.IndexOf("(") + 2) 'Used to bypass the first CombatLogNames packet that contain only 1 entry
If (check.IndexOf("(") <> -1 And index1 <> "" And index2 <> " ") Then 'Stricter Filters for CombatLogNames
Dim endCLN As Integer = 0 'Signifies the end of CombatLogNames Packet
Dim x As Integer = 0 'Counter for array
While (endCLN = 0 And streambuff <> "---- CNETMsg_Tick") 'Loops until the end keyword for CombatLogNames is seen
streambuff = sr.ReadLine 'Reads a new line to flush out "CombatLogNames flags:0x1" which is unneeded
If ((streambuff.Contains("---- CNETMsg_Tick") = True) Or (streambuff.Contains("ResponseKeys flags:0x0 ") = True)) Then
endCLN = 1 'Value change to determine end of CombatLogName packet
Else
ReDim Preserve CombatLogNames(x) 'Resizes the array while preserving the values
searcher = streambuff.Trim.Remove(streambuff.IndexOf("(") - 5).Remove(0, _
streambuff.Trim.Remove(streambuff.IndexOf("(")).IndexOf("'")) 'Additional filtering to get only valuable data
CombatLogNames(x) = search(searcher)
x += 1 '+1 to Array counter
End If
End While
Else
'MsgBox("Something went wrong, Flame the coder of this program!!") 'Bug Testing code that is disabled
End If
Else
End If
If (sr.EndOfStream = True) Then
ReDim GlobalArr(CombatLogNames.Length - 1) 'Resizing the Global array to prime it for copying data
Array.Copy(CombatLogNames, GlobalArr, CombatLogNames.Length) 'Just copying the array to make it global
End If
Loop
You CAN set the BaseStream to the desired reading position, you just cant set it to a specfic LINE (because counting lines requires to read the complete file)
Using sw As New StreamWriter("foo.txt", False, System.Text.Encoding.ASCII)
For i = 1 To 100
sw.WriteLine("the quick brown fox jumps ovr the lazy dog")
Next
End Using
Using sr As New StreamReader("foo.txt", System.Text.Encoding.ASCII)
sr.BaseStream.Seek(-100, SeekOrigin.End)
Dim garbage = sr.ReadLine ' can not use, because very likely not a COMPLETE line
While Not sr.EndOfStream
Dim line = sr.ReadLine
Console.WriteLine(line)
End While
End Using
For any later read attempt on the same file, you could simply save the final position (of the basestream) and on the next read to advance to that position before you start reading lines.
What worked for me was skipping first 4M lines (just a simple if counter > 4M surrounding everything inside the loop), and then adding background workers that did the filtering, and if important added the line to an array, while main thread continued reading the lines. This saved about third of the time at the end of a day.