Appending a csv file - vb.net

Dim NumberOfRecords As Integer
Sub Main()
Call ListTowns()
End Sub
Sub ListTowns()
Dim FileName As String
Dim MyFormat As String = "{0, -22} {1, -16} {2, -8} {3, -8}"
FileName = "Towns.csv"
Dim AllRecords As String() = System.IO.File.ReadAllLines(FileName)
Dim TownList = From record In AllRecords
Let field = record.Split(",")
Select New With {.Name = field(0), .County = field(1), .Population = field(2), .Area = field(3)}
For Each Town In TownList
Console.WriteLine(String.Format(MyFormat, Town.Name, Town.County, Town.Population, Town.Area))
Next
NumberOfRecords = TownList.Count
Console.ReadLine()
End Sub
Sub AddRecord()
Dim FileName As String
FileName = "C:\Users\Omar\Desktop\Towns.csv"
FileOpen(1, FileName, OpenMode.Random)
Dim NewRecord As String
Console.WriteLine("Enter the record you want add")
NewRecord = Console.ReadLine()
FilePut(1, NewRecord, NumberOfRecords + 1)
FileClose(1)
Console.WriteLine("The record has been added")
Console.ReadLine()
End Sub
At the moment the program can list the contents of the csv file however with regard to the AddRecord() Sub, when I input the data Test,Test,Test,Test it is adding this record to the file properly and is overwriting the first record.
How do I fix this?

OpenMode.Random is used for reading/writing files that have a fixed width record. It is unclear from your post if you actually have fixed width records (my hunch is you don't). At any rate, to properly read/write in random mode you'd have to declare a structure that defines the length of each record and also tell the system the length of the record when you open it (so that it can move that many bytes to the desired record position you want to read/write!). Follow the links listed in this page titled Random File Access to learn how to do this.
If you don't actually have fixed width records, but instead have variable length records (one record per line, and the lines are different lengths), then you can simply store all the lines in a List(Of String) and add the new record to the end of the List. Now you simply overwrite the entire file with System.IO.File.WriteAllLines(), similar to what you did with ReadAllLines(). With this approach, you can modify any of the records in the List and then simply overwrite the entire file to update the physical file.
Here's a quick example of what the second approach might look like:
Module Module1
Public AllRecords As New List(Of String)
Public FileName As String = "C:\Users\Omar\Desktop\Towns.csv"
Public Sub Main()
LoadTowns()
ListTowns()
AddRecord()
ListTowns()
End Sub
Public Sub LoadTowns()
AllRecords.Clear()
AllRecords.AddRange(System.IO.File.ReadAllLines(FileName))
End Sub
Public Sub ListTowns()
Dim MyFormat As String = "{0, -22} {1, -16} {2, -8} {3, -8}"
Dim TownList = From record In AllRecords
Let field = record.Split(",")
Select New With {.Name = field(0), .County = field(1), .Population = field(2), .Area = field(3)}
For Each Town In TownList
Console.WriteLine(String.Format(MyFormat, Town.Name, Town.County, Town.Population, Town.Area))
Next
Console.ReadLine()
End Sub
Public Sub AddRecord()
Console.WriteLine("Enter the record you want add")
Dim NewRecord As String = Console.ReadLine()
AllRecords.Add(NewRecord)
System.IO.File.WriteAllLines(FileName, AllRecords.ToArray)
Console.WriteLine("The record has been added")
Console.ReadLine()
End Sub
End Module

Related

vb.net word cloud indexing

I use a wordcloud based on a list of words and their frequencies. I load the list from a text file and display them in a Listview and image. When the textfile is not indexed (the highest frequencies first) the word cloud doesn't make the words with the highest counts the biggest.
Is there a way to load the words, highest frequencies first, without having to change the list?
Imports WordCloudGen = WordCloud.WordCloud
Imports System.IO
Public Class WordCloud
Private Sub WordCloud_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim lines = File.ReadLines("C:\Users\Gebruiker\Downloads\Words.txt")
Dim Words As New List(Of String) '({100})
Dim Frequencies As New List(Of Integer) '({100})
Dim textValue As String()
Dim items As New List(Of ListViewItem)
For Each line In lines
textValue = line.Split(New Char() {","})
Words.Add(textValue(0))
Frequencies.Add(Integer.Parse(textValue(1)))
items.Add(New ListViewItem(New String() {textValue(0).ToString, textValue(1).ToString}, 0))
Next
ListView1.Items.AddRange(items.ToArray)
Dim wc As WordCloudGen = New WordCloudGen(600, 400)
Dim i As Image = wc.Draw(Words, Frequencies)
ResultPictureBox.Image = i
End Sub
When the textfile is not indexed (the highest frequencies first) the word cloud doesn't make the words with the highest counts the biggest. Is there a way to load the words, highest frequencies first, without having to change the list?
I would recommend a new class to hold your data then you can sort anything you need much easier.
Create a new class: WordsFrequencies
Public Class WordsFrequencies
Public Property Word As String
Public Property Frequency As Integer
End Class
Change your WordCloud_Load routine as below:
Dim WordsFreqList As New List(Of WordsFrequencies)
For Each line As String In File.ReadLines("C:\Users\Gebruiker\Downloads\Words.txt")
Dim splitText As String() = line.Split(","c)
If splitText IsNot Nothing AndAlso splitText.Length = 2 Then
Dim wordFrq As New WordsFrequencies
Dim freq As Integer
wordFrq.Word = splitText(0)
wordFrq.Frequency = If(Integer.TryParse(splitText(1), freq), freq, 0)
WordsFreqList.Add(wordFrq)
End If
Next
If WordsFreqList.Count > 0 Then
' Order the list based on the Frequency
WordsFreqList = WordsFreqList.OrderByDescending(Function(w) w.Frequency).ToList
' Add the sorted items to the listview
WordsFreqList.ForEach(Sub(wf)
ListView1.Items.Add(New ListViewItem(New String() {wf.Word, wf.Frequency.ToString}, 0))
End Sub)
End If
In the above, I would recommend doing a simple For loop with File.ReadLines, this is so you don't have to load the whole file in memory if you're just getting data and parsing it. I'm using the OrderByDescending Method which is part of System.Linq namespace.
As far as this: Dim i As Image = wc.Draw(Words, Frequencies) you then could do something like:
Dim i As Image = wc.Draw(WordsFreqList.Select(Function(wf) wf.Word), WordsFreqList.Select(Function(wf) wf.Frequency))
This will project the Word's into an IEnumerable(String) and then Frequency into an IEnumerable(Integer).

Read from text file, store data into corresponding structure every 6 element, and then form an array

The text file contains the following: inside the [], anything inside () was not in the text file, just for clarification
[1(ID)
Jimmy(First name)
Paul (Last name)
78 (marks1)
80 (marks2)
92 (marks3)
2
Ben
James
67
82
73
]
I created a structure that holds student details including their name, id, marks in each subject.
Private Structure StudInfo
Public FName As String
Public LName As String
Public StudentId As Integer
Public ScMark As Integer
Public EnMark As Integer
Public MaMark As Integer
The program needs to read the first six elements in a row, storing each element into the corresponding structure type, then let it become the first element of an array"students()", and then next six elements, let it become the second element of that array. I have no idea how to use loops to do that.
Private Sub Button4_Click(sender As Object, e As EventArgs) Handles Button4.Click
'create an array that hold student details
Dim Students() As StudInfo
' read from text file
Dim FileNum As Integer = FreeFile()
Dim TempS As String = ""
Dim TempL As String
FileOpen(FileNum, "some.text", OpenMode.Input)
Do Until EOF(FileNum)
TempL = LineInput(FileNum)
TempS = TempL + vbCrLf
Loop
End Sub
Thank you.
You have to use a BinaryReader (which takes a IO.Stream as it's constructor), then you can read the data type you want, into the variable you want.
The problem you will have is the data will not be searchable (ie. you cannot read the 30th record, unless you physically read the first 29, because the strings are of variable length, and therefore the record is variable length), this also applies to modifying a record (you cant make it bigger, because it will overwrite the next record).
The answer is to work with fixed length records, or field offsets or fixed length strings. Then you will have a records of predictable size, and you can determine the amount of records by dividing the file length by the record size.
Hope this helps.
You could try something like this:
Public Class Form1
Private Students As New List(Of StudInfo)
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Students.Clear()
Dim fileName = "c:\some folder\directory\someFile.txt"
Using sr As New System.IO.StreamReader(fileName)
Dim value As Integer
Dim strValue As String
While Not sr.EndOfStream
Try
Dim student As New StudInfo
strValue = sr.ReadLine().Trim("[]")
If Integer.TryParse(strValue, value) Then
student.StudentId = value
Else
MessageBox.Show("Error Converting StudentID to Integer")
Exit Sub
End If
student.FName = sr.ReadLine().Trim("[]")
student.LName = sr.ReadLine().Trim("[]")
strValue = sr.ReadLine().Trim("[]")
If Integer.TryParse(strValue, value) Then
student.ScMark = value
Else
MessageBox.Show("Error Converting ScMark to Integer")
Exit Sub
End If
strValue = sr.ReadLine().Trim("[]")
If Integer.TryParse(strValue, value) Then
student.EnMark = value
Else
MessageBox.Show("Error Converting EnMark to Integer")
Exit Sub
End If
strValue = sr.ReadLine().Trim("[]")
If Integer.TryParse(strValue, value) Then
student.MaMark = value
Else
MessageBox.Show("Error Converting MaMark to Integer")
Exit Sub
End If
Students.Add(student)
Catch ex As Exception
MessageBox.Show("Error reading file. All records may not have been created.")
End Try
End While
MessageBox.Show("Done!")
End Using
End Sub
Private Class StudInfo
Public FName As String
Public LName As String
Public StudentId As Integer
Public ScMark As Integer
Public EnMark As Integer
Public MaMark As Integer
End Class
End Class
It depends a bit on the exact format of your text file.
If the file only contains the data for the two students (no brackets or blank lines), then all you need to do is to open the file and read 6 lines, add the data to your structure. and read the next 6 lines. If you have an undetermined number of students in the text file, then you would be better using a List. Other wise you're goiing to have to use extra processing time to Redim the array each time you want to add a student and keep track of the array size an all sorts of messing around.
However. Lets go with the most straightforward answer and assume your data has no brackets or blank lines and that there are only two students.
This code should work just fine. If you have a different definite number of students, then you will need to change the size of the Students array.
I'm also assuming that the data is correctly formatted and there are no non-numeric characters are in the lines where there shouldn't be.
Private Sub ReadStudentInfo()
'create an array that hold student details
Dim Students(2) As StudInfo
Dim index As Integer = 0
' read from text file
Dim datafile As New StreamReader("some.text")
Do Until datafile.EndOfStream
Dim tempStudent As StudInfo
With tempStudent
Integer.TryParse(datafile.ReadLine, .StudentId)
.FName = datafile.ReadLine
.LName = datafile.ReadLine
Integer.TryParse(datafile.ReadLine, .ScMark)
Integer.TryParse(datafile.ReadLine, .EnMark)
Integer.TryParse(datafile.ReadLine, .MaMark)
End With
Students(index) = tempStudent
index = index + 1
Loop
End Sub
If your text file does contain blank lines, just insert
datafile.ReadLine()
between each line of data - like this
Integer.TryParse(datafile.ReadLine, .ScMark)
datafile.ReadLine()
Integer.TryParse(datafile.ReadLine, .EnMark)
If you have the brackets in your text file, then you'll need to add extra code to remove them.

How to use a Sub value in a second Sub

I am new to VB.net and really need bigger brains for this:
(all code added in one module)
I have a random function (that is giving me a random text value).
This is how I call the random function:
Dim IpAddresses As String() = My.Resources.BrainAnswer.Split(New String() {Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries)
Dim RandomIpAddress As String = IpAddresses(GetRandom(IpAddresses.Length))
Now, I have a Sub that takes the Random text value and displays that in a Richtextbox, with a typewriter effect:
Sub type()
Dim thread As New Thread(AddressOf voice)
thread.Start()
Form1.RichTextBox1.Refresh()
Form1.count_ = 1
Form1.RichTextBox1.Text = Form1.str_
Form1.RichTextBox1.Clear()
Form1.str_ = RandomIpAddress
Form1.Timer1.Enabled = True
End Sub
I also have a Thread that I want to call in Sub Type()
Private Sub voice()
Dim TheSpeaker As New Speech.Synthesis.SpeechSynthesizer()
TheSpeaker.SelectVoiceByHints(Synthesis.VoiceGender.Female)
TheSpeaker.Speak(RandomIpAddress)
End Sub
My problem is: how to get the RandomIpAddress in both Sub type() and Private Sub voice?
If I'm using the:
Dim IpAddresses As String() = My.Resources.BrainAnswer.Split(New String() {Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries)
Dim RandomIpAddress As String = IpAddresses(GetRandom(IpAddresses.Length))
inside the Module, then my code is running ONCE correctly. After that the Random code is not working any more (it's loading the same text) - never changing the result (no random).
If I am going to move the "Dim" code inside SUB and Thread, then I will have a random text added in richtextbox and another in Thread. So is doing a random result for both. I just want to get the same random result in both!
Here is my full code:
Imports System.Threading
Imports System.Speech
Imports System.Speech.Recognition
Module brain
Public str_ As String
Private rdm As New Random
Private Function GetRandom(max As Integer) As Integer
If InStr(UCase(Form1.TextBox1.Text), "HELLO MOTHER") Then
Dim theTime As DateTime
theTime = Now.ToLongTimeString
If theTime >= #6:00:00 AM# AndAlso theTime <= #9:59:59 AM# Then
Return rdm.Next(0, 3)
Else
Return rdm.Next(3, 6)
End If
End If
If InStr(UCase(Form1.TextBox1.Text), "HOW ARE YOU") Then
Return rdm.Next(6, 8)
End If
If InStr(UCase(Form1.TextBox1.Text), "WHO ARE YOU") Then
Return rdm.Next(8, 11)
End If
If InStr(UCase(Form1.TextBox1.Text), "YOUR NAME") Then
Return rdm.Next(11, 13)
End If
If InStr(UCase(Form1.TextBox1.Text), "WHAT ARE YOU") Then
Return rdm.Next(13, 16)
End If
If InStr(UCase(Form1.TextBox1.Text), "WHAT DO YOU DO") Then
Return rdm.Next(16, 18)
End If
If InStr(UCase(Form1.TextBox1.Text), "WHAT CAN YOU DO") Then
Return rdm.Next(16, 18)
End If
If InStr(UCase(Form1.TextBox1.Text), "HELP ME") Then
Return rdm.Next(16, 18)
End If
If InStr(UCase(Form1.TextBox1.Text), "YOUR MISSION") Then
Return rdm.Next(18, 19)
End If
End Function
Dim IpAddresses As String() = My.Resources.BrainAnswer.Split(New String() {Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries)
Dim RandomIpAddress As String = IpAddresses(GetRandom(IpAddresses.Length))
Sub type()
Dim thread As New Thread(AddressOf voice)
thread.Start()
Form1.RichTextBox1.Refresh()
Form1.count_ = 1
Form1.RichTextBox1.Text = Form1.str_
Form1.RichTextBox1.Clear()
Form1.str_ = RandomIpAddress
Form1.Timer1.Enabled = True
End Sub
Private Sub voice()
Dim TheSpeaker As New Speech.Synthesis.SpeechSynthesizer()
TheSpeaker.SelectVoiceByHints(Synthesis.VoiceGender.Female)
Dim cleanString As String = Replace(RandomIpAddress, ".", " ")
TheSpeaker.Speak(cleanString)
End Sub
End Module
It seems either you are not getting your task clear or not getting us clear about it.
There is a handful of remarks to pay for this strip of code
Your variable max isn't used in your function GetRandom, that is not a foretelling sign in favor of same function's results.
I believe you are missing Return rdm.Next(19, max) somewhere in your GetRandom(max) function, just a prediction that has a strong likelihood to be applicable
RandomIpAddress is declared a static variable, while you are using it as a function.
Public Delegate Function newfunction() as string
Public RandomIpAddress As newfunction = Function() IpAddresses(GetRandom(IpAddresses.Length))
Thus the use of it would differ to:
Form1.str_ = RandomIpAddress()
And
Dim cleanString As String = Replace(RandomIpAddress(), ".", " ")
Threads are independant entities, they dont have access to other forms' ressources unless you share them.
Declaration of your textbox must be:
Friend Shared WithEvents TextBox1 As TextBox .

read specific values from text file

I have the following visual basic code, which is part of a custom class. I want a simple and effective way(use little computer resources) to assign the "value1" value(100) to "_field1","value2" value(8) to "_field2" etc. Any nice ideas? thanks
Private Sub readcrnFile()
'read files and assing values to the properties
Dim sr As New IO.StreamReader(_fileName)
_field1 = sr.ReadToEnd()
_field2 = sr.ReadToEnd()
_field3 = sr.ReadToEnd()
sr.Close()
End Sub
where _fileName is a full path to a text file which looks like this:
value1: 100
value2: 8
value3: 80
Private Sub readcrnFile()
Dim lines = File.ReadLines(_fileName)
For Each line In lines
Dim val = line.Split(":")(1).Trim
'do something with val?
Next
End Sub
Returning a dictionary is trivial:
Private Sub readcrnFile()
Dim dict = File.ReadLines(_fileName).Select(Function(line) line.Split(":")).ToDictionary(Function(parts) parts(0).Trim, Function(parts) parts(1).Trim)
Debug.WriteLine(dict("value1")) 'will print 100
End Sub
Change your _field1, _field2 and _field3 variables to a List(Of String) (i.e. named field) and access to each field using its index (field(0), field(1), field(2)).
Dim field As New List(Of String)
Private Sub readcrnFile()
For Each line In File.ReadAllLines(_filename)
For i = 1 To 3
If line.Contains("value" & i) Then
field.Add(line.Substring(line.IndexOf(":") + 2))
End If
Next
Next
End Sub

Search engine in vb.net

I am building a search engine in vb.net which would have to search for a word entered by the user in 40 text files within the project directory.
It should return the results as the total number of matches (text files) and the number of times this word is in each file. Any suggestions for a start would be grateful.
Regards.
get a list of the files in the directory with something like: Directory.GetFiles(ProjectDir, "*.*"), then read each file in the list like this:
Dim sr As StreamReader = New StreamReader(fileName)
Dim line As String
Do
line = sr.ReadLine()
scan the line and count
Loop Until line Is Nothing
sr.Close()
Try this code, in a console application, not only could find a word
even you can get the results using a RegEx Expression.
Class TextFileInfo
Public File As System.IO.FileInfo
public Count As Integer
public FileText As String
public ItMatch as Boolean = False
Sub New (FileFullName as String,WordPattern as String)
File = new System.IO.FileInfo(FileFullName)
Using Fs As System.IO.StreamReader(File.FullName)
FileText = Fs.ReadToEnd()'//===>Read Text
End Using
Count = _CountWords(WordPattern,FileText)
ItMatch = Count > 0
End Sub
Public Sub DisplayInfo()
System.Console.WriteLine("File Name:" + File.Name)
System.Console.WriteLine("Matched Times:" & Count)
End Sub
Private Function _CountWords(Word As String,Text As String) as Integer
Dim RegEx as System.Text.RegularExpressions.Regex(Word)
return RegEx.Matches(Text).Count'//===>Returns how many times this word match in the Text
End Fuction
End Class
Public Function SearchEngine(PatternWord As String,RootDirectory As String) List(Of TextFileInfo)
Dim MatchedFiles As New List(Of TextFileInfo)
Dim RootDir As New System.IO.DirectoryInfo(RootDirectory)
For Each iTextFile as System.IO.FileInfo In RootDir.GetFiles("*.txt")
'//===>Create a object of TextFileInfo and check if the file contains the word
Dim iMatchFile as New TextFileInfo(iTextFiles.FullName,PatternWord)
If iMatchFile.ItMatch Then
'//===>Add the object to the list if it has been matches
MatchedFiles.Add(iMatchFile)
End If
Loop
retur MatchedFiles '//===>Return the results of the files that has the matched word
End Function
Sub Main()
Dim SearchResults as List(Of TextFileInfo) = SearchEngine("JajajaWord","C:\TextFiles\")
For Each iSearch As TextFileInfo In SearchResults
iSearch.DisplayInfo()
Loop
End Sub