Progress bar with VB.NET Console Application - vb.net

I've written a parsing utility as a Console Application and have it working pretty smoothly. The utility reads delimited files and based on a user value as a command line arguments splits the record to one of 2 files (good records or bad records).
Looking to do a progress bar or status indicator to show work performed or remaining work while parsing. I could easily write a <.> across the screen within the loop but would like to give a %.
Thanks!

Here is an example of how to calculate the percentage complete and output it in a progress counter:
Option Strict On
Option Explicit On
Imports System.IO
Module Module1
Sub Main()
Dim filePath As String = "C:\StackOverflow\tabSeperatedFile.txt"
Dim FileContents As String()
Console.WriteLine("Reading file contents")
Using fleStream As StreamReader = New StreamReader(IO.File.Open(filePath, FileMode.Open, FileAccess.Read))
FileContents = fleStream.ReadToEnd.Split(CChar(vbTab))
End Using
Console.WriteLine("Sorting Entries")
Dim TotalWork As Decimal = CDec(FileContents.Count)
Dim currentLine As Decimal = 0D
For Each entry As String In FileContents
'Do something with the file contents
currentLine += 1D
Dim progress = CDec((currentLine / TotalWork) * 100)
Console.SetCursorPosition(0I, Console.CursorTop)
Console.Write(progress.ToString("00.00") & " %")
Next
Console.WriteLine()
Console.WriteLine("Finished.")
Console.ReadLine()
End Sub
End Module

1rst you have to know how many lines you will expect.
In your loop calculate "intLineCount / 100 * intCurrentLine"
int totalLines = 0 // "GetTotalLines"
int currentLine = 0;
foreach (line in Lines)
{
/// YOUR OPERATION
currentLine ++;
int progress = totalLines / 100 * currentLine;
///print out the result with the suggested method...
///!Caution: if there are many updates consider to update the output only if the value has changed or just every n loop by using the MOD operator or any other useful approach ;)
}
and print the result on the same posititon in your loop by using the SetCursor method
MSDN Console.SetCursorPosition
VB.NET:
Dim totalLines as Integer = 0
Dim currentLine as integer = 0
For Each line as string in Lines
' Your operation
currentLine += 1I
Dim Progress as integer = (currentLine / totalLines) * 100
' print out the result with the suggested method...
' !Caution: if there are many updates consider to update the output only if the value has changed or just every n loop by using the MOD operator or any other useful approach
Next

Well The easiest way is to update the progressBar variable often,
Ex: if your code consist of around 100 lines or may be 100 functionality
after each function or certain lines of code update progressbar variable with percentage :)

Related

Readline Error While Reading From .txt file in vb.net

I have a Streamreader which is trowing an error after checking every line in Daycounts.txt. It is not a stable txt file. String lines in it are not stable. Count of lines increasing or decresing constantly. Thats why I am using a range 0 to 167. But
Here is the content of Daycounts.txt: Daycounts
Dim HourSum as integer
Private Sub Change()
Dim R As IO.StreamReader
R = New IO.StreamReader("Daycounts.txt")
Dim sum As Integer = 0
For p = 0 To 167
Dim a As String = R.ReadLine
If a.Substring(0, 2) <> "G." Then
sum += a.Substring(a.Length - 2, 2)
Else
End If
Next
HourSum = sum
R.Close()
End Sub
If you don't know how many lines are present in your text file then you could use the method File.ReadAllLines to load all lines in memory and then apply your logic
Dim HourSum As Integer
Private Sub Change()
Dim lines = File.ReadAllLines("Daycounts.txt")
Dim sum As Integer = 0
For Each line In lines
If line.Substring(0, 2) <> "G." Then
sum += Convert.ToInt32(line.Substring(line.Length - 2, 2))
Else
....
End If
Next
HourSum = sum
End Sub
This is somewhat inefficient because you loop over the lines two times (one to read them in, and one to apply your logic) but with a small set of lines this should be not a big problem
However, you could also use File.ReadLines that start the enumeration of your lines without loading them all in memory. According to this question, ReadLines locks writes your file until the end of your read loop, so, perhaps this could be a better option for you only if you don't have something external to your code writing concurrently to the file.
Dim HourSum As Integer
Private Sub Change()
Dim sum As Integer = 0
For Each line In File.ReadLines("Daycounts.txt")
If line.Substring(0, 2) <> "G." Then
sum += Convert.ToInt32(line.Substring(line.Length - 2, 2))
Else
....
End If
Next
HourSum = sum
End Sub
By the way, notice that I have added a conversion to an integer against the loaded line. In your code, the sum operation is applied directly on the string. This could work only if you have Option Strict set to Off for your project. This setting is a very bad practice maintained for VB6 compatibility and should be changed to Option Strict On for new VB.NET projects

A loop exits prematurely when using StreamReader

It's been a long time since I've programmed. I'm writing a form in VB.NET, and using StreamReader to read a text file and populate an 2D array. Here is the text file:
あかさたなはまやらわん
いきしちにひみ り
うくすつぬふむゆる
えけせてねへめ れ
おこそとのほもよろを
And here is the loop, which is within the Load event.
Dim Line As String
Dim Row As Integer = 0
Using sReader As New IO.StreamReader("KanaTable.txt")
Do
Line = sReader.ReadLine
For i = 0 To Line.Length - 1
KanaTable(Row, i) = Line(i)
Next
Row += 1
Loop Until sReader.EndOfStream
End Using
The problem is, once the i in the For Loop reaches 10, it completes the loop and skips the other lines, even when I have a breakpoint. Can you let me know what's probably going on here?
I've figured out the problem, it was very simple. The array declaration for KanaTable:
Dim KanaTable(4, 9) As Char
should have been
Dim KanaTable(4, 10) As Char
Because there was one less space in the array than there should have been, the debugger must have been throwing an IndexOutOfRange which I couldn't see, because, stupid Windows bug (thanks to Bradley Uffner for pointing out this bug.)
If you can use an array of arrays or a list of arrays (List(Of Char())), you can get this down to a single line of code:
Dim KanaTable()() As Char = IO.File.ReadLines("KanaTable.txt").Select(Function(line) line.ToCharArray()).ToArray()
If that's too complicated for you, we can at least simplify the existing code:
Dim KanaTable As New List(Of Char())
Dim Line As String
Using sReader As New IO.StreamReader("KanaTable.txt")
Line = sReader.ReadLine()
While Line IsNot Nothing
KanaTable.Add(Line.ToCharArray())
Line = sReader.ReadLine()
End While
End Using
I can't see an error immediately, but you could try to adapt your code to this:
Using reader As New IO.StreamReader("KanaTable.txt")
Do
line= reader.ReadLine()
If line = Nothing Then
Exit Do
End If
For i = 0 To Line.Length - 1
KanaTable(Row, i) = Line(i)
Next
Row += 1
Loop
End Using

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.

Is there a better way to count the lines in a text file?

Below is what I've been using. While it does work, my program locks up when trying to count a rather large file, say 10,000 or more lines. Smaller files run in no time.
Is there a better or should I say faster way to count the lines in a text file?
Here's what I'm currently using:
Dim selectedItems = (From i In ListBox1.SelectedItems).ToArray()
For Each selectedItem In selectedItems
ListBox2.Items.Add(selectedItem)
ListBox1.Items.Remove(selectedItem)
Dim FileQty = selectedItem.ToString
'reads the data file and returns the qty
Dim intLines As Integer = 0
'Dim sr As New IO.StreamReader(OpenFileDialog1.FileName)
Dim sr As New IO.StreamReader(TextBox1_Path.Text + "\" + FileQty)
Do While sr.Peek() >= 0
TextBox1.Text += sr.ReadLine() & ControlChars.CrLf
intLines += 1
Loop
ListBox6.Items.Add(intLines)
Next
Imports System.IO.File 'At the beginning of the file
Dim lineCount = File.ReadAllLines("file.txt").Length
See this question.
Even if you make your iteration as efficient as can be, if you hand it a large enough file you're going to make the application freeze while it performs the work.
If you want to avoid the locking, you could spawn a new thread and perform the work asynchronously. If you're using .NET 4.0 you can use the Task class to make this very easy.
TextBox2.Text = File.ReadAllLines(scannerfilePath).Length.ToString()

Help Visual Basic mixing characters

I'm making an application that will change position of two characters in Word.
Imports System.IO
Module Module1
Sub Main()
Dim str As String = File.ReadAllText("File.txt")
Dim str2 As String() = Split(str, " ")
For i As Integer = 0 To str2.Length - 1
Dim arr As Char() = CType(str2(i), Char())
For ia As Integer = 0 To arr.Length() - 1 Step 2
Dim pa As String
pa = arr(ia + 1)
arr(ia + 1) = arr(ia)
arr(ia) = pa
Next ia
For ib As Integer = 0 To arr.Length - 1
Console.Write(arr(ib))
File.WriteAllText("File2.txt", arr(ib))
Next ib
File.WriteAllText("File2.txt", " ")
Console.Write(" ")
Next i
Console.Read()
End Sub
End Module
For example:
Input: ab
Output: ba
Input: asdasd asdasd
Output: saadds saadds
Program works good, it is mixing characters good, but it doesn't write text to the file. It will write text in console, but not in file.
Note: Program is working only with words that are divisible by 2, but it's not a problem.
Also, it does not return any error message.
Your code is overwriting the file that you have already written with a single space (" ") each time round.
You should only open the file once, and append to it using a stream writer:
Using output = File.CreateText("file2.txt")
' Put the for loop here.
End Using
There are some other things wrong with your code. Firstly, use For Each instead of For, this makes your code much more simple and readable. Secondly, try to avoid For loops altogether where possible. For instance, instead of iterating over the characters to output them one at a time, just create a new string from the char array, and write that:
Dim shuffledWord As New String(arr)
output.Write(shuffledWord)
Some of your types are plain wrong, i.e. you are using String in places instead of Char. You should always use Option Strict On. Then the compiler will not tolerate such code.
You should also prefer to use framework methods over VB-specific methods. This makes it easier to understand for C# programmers, and also makes it easier to translate and change (that is, use the Split method of strings instead of a free function, use ToCharArray instead of a cast to Char() …).
Finally, use meaningful variable names. str, str2 and arr are particularly cryptic because they don’t tell the reader of the code anything of interest about the variables.
Sub Main()
Dim text As String = File.ReadAllText("File.txt")
Dim words As String() = str.Split(" "c)
Using output = File.CreateText("file2.txt")
For Each word In words
dim wordChars = word.ToCharArray()
For i As Integer = 0 To wordChars.Length - 1 Step 2
Dim tmp As Char = wordChars(i + 1)
wordChars(i + 1) = wordChars(i)
arr(i) = tmp
Next
Dim shuffledWord As New String(wordChars)
output.Write(shuffledWord + " ")
Console.Write(huffledWord + " ")
Next
End Using
Console.Read()
End Sub