How do I add text to a specific line in a text file in visual basic? - vb.net

I am trying to create a program for an a level project using visual studio 2010 or 2017 at home, and am using a console application. What I need it to do is to create a text file with the students details which are entered to the program by the teacher. Then the teacher needs to be able to enter a specific student ID number and be able to add a score to the end of the record for this student.
This is my code so far and works as it should. But it only allows the teacher to enter the students details and writes it to the text file.
Dim studentID As String
Dim studentname As String
Dim classname As String
Dim Filename As String
Filename = “Students.txt”
FileOpen(1, Filename, OpenMode.Append)
Console.WriteLine(" Student Setup")
Console.WriteLine()
Console.WriteLine()
Console.Write("Student ID: ")
studentID = Console.ReadLine
Console.Write("Student Name: ")
studentname = Console.ReadLine
Console.WriteLine()
Console.Write("Class : ")
classname = Console.ReadLine
WriteLine(1, studentID, studentname, classname)
Console.WriteLine("Student successfully setup.")
Now I want to be able to allow the teacher to type in a students ID number and a score, and then write the score to the end of the specific line in the text file which begins with the students ID number.
An example of the text file will look like this:
"12345","Adam","A"
"67534","Alice","A"
"21456","Bob","A"
"98765","Charles","A"
"87654","Dennis","A"
Then if the teacher types in 21456 as the students ID number to add score to, and a score of 6 then it will add the number 6 to the end of this line, so that the line of the text file will now look like this:
"21456","Bob","A","6"
I have attempted to do this with the following code, which I got from another forum and tried to alter to do what I wanted to do:
Dim score As Integer
Dim studentIDenter As String
Dim count As Integer
Console.Write("Student ID to add score to: ")
studentIDenter = Console.ReadLine
Console.Write("Student score: ")
score = Console.readline
Dim lines() As String = System.IO.File.ReadAllLines(Filename)
For count = 0 To lines.Length - 1
If lines(count).StartsWith(studentIDenter) Then
lines(count) = studentID & studentname & classname & score
System.IO.File.WriteAllLines(Filename, lines)
End If
Next
FileClose(1)

I assume the user is NOT entering quotes around the student number and score.
You need to add the quotes in your check for a match at the beginning of the line. Additionally, you need to add a comma and quotes around the score when you add them to the end of the line.
Change to:
For count = 0 To lines.Length - 1
If lines(count).StartsWith(Chr(34) & studentIDenter & Chr(34)) Then
lines(count) = lines(count) & "," & Chr(34) & score & Chr(34)
System.IO.File.WriteAllLines(Filename, lines)
Exit For ' found the record, no need to keep looking
End If
Next

I have created the following:
Sub Main()
Dim studentID As String
Dim studentname As String
Dim classname As String
Dim addScoreSelection As String
Dim inputSID As String
Dim Filename As String
Filename = “Students.txt”
FileOpen(1, Filename, OpenMode.Append)
Console.WriteLine("Add a Score? Y/N")
addScoreSelection = Console.ReadLine.ToLower()
If addScoreSelection = "Y".ToLower() Then
FileClose(1)
Console.WriteLine("Enter a student ID: ")
inputSID = Console.ReadLine()
AddScore(inputSID)
ElseIf addScoreSelection = "N".ToLower() Then
Console.WriteLine("Student Setup")
Console.WriteLine()
Console.WriteLine()
Console.Write("Student ID: ")
studentID = Console.ReadLine
Console.Write("Student Name: ")
studentname = Console.ReadLine
Console.WriteLine()
Console.Write("Class : ")
classname = Console.ReadLine
WriteLine(1, studentID, studentname, classname)
Console.WriteLine("Student successfully setup.")
Else
Console.WriteLine("No selection made. Exiting.")
Process.GetCurrentProcess.Kill()
End If
End Sub
Sub AddScore(ByVal ID As String)
Dim Filename As String
Filename = “Students.txt”
Dim fileContent As String()
Dim index As Integer = 0
fileContent = IO.File.ReadAllLines(Filename)
For Each line As String In fileContent
If line.Contains(ID) Then
fileContent(index) = fileContent(index) & ",""6""" 'Pass in a ByVal param for this
Exit For
End If
index += 1
Next
IO.File.WriteAllLines(Filename, fileContent)
End Sub
I have added the sub routine AddScore that takes a student ID as a string param, I then use this parameter in a .Contains() to determine WHEN I have hit the line we are interested in in the text file.
I create a string array by reading all the text from the file
fileContent = IO.File.ReadAllLines(Filename)
I iterate through the array looking for the line in question, once the line is found I append the score to the end of the line (You will probably want to actually use a param here)
For Each line As String In fileContent
If line.Contains(ID) Then
fileContent(index) = fileContent(index) & ",""6""" 'Pass in a ByVal param for this
Exit For
End If
index += 1
Next
I then write the string array to the file
IO.File.WriteAllLines(Filename, fileContent)
This solves your issue I believe, but personally I am not happy with this. I don't like the idea of grabbing a whole lot of text and putting it into memory (imagine if we were reading a 5GB file...). The best solution in the long run would be to use the FileStream class to read/write it and find the line and append the text on said line.

Related

Vb.net Data is not being incremented and added to list

I'm having an issue trying to create a program that takes user input for a text file's location containing medical records. The diseases and number of patients are being added to a list. I'm having an issue where my console is printing 0 for both the total of XX unique diseases and YYY patient encounters. I am not getting any errors, just not the correct output.
I believe my issue is in my processData() sub, however I am unsure why it's printing back 0. Also, how do I go about keeping track of duplicate diseases that are added to the list as I'm trying to add a counter next to each time the disease is seen.
Sample from Disease.txt
3710079 JUDITH CLOUTIER 2012-08-04 Spastic Colonitis
3680080 VIRGINIA ALMOND 2012-07-25 Chronic Phlegm
3660068 ELLEN ENGLEHARDT 2012-04-06 Whooping Cough
3810076 LILLIAN KEMMER 2014-07-04 Scurvy
3630055 TERESA BANASZAK 2012-06-15 Scurvy
Output:
There were a total of 0 unique diseases observed.
A total of 0 patient encounters were held
Main():
' Global variables
Dim inputFile As String
Dim patientCounter = 0
Dim diseaseList As New List(Of String)
Dim dateList As New List(Of Date)
Sub Main()
Dim reportFile As String
Dim yn As String
Console.ForegroundColor = ConsoleColor.Yellow
Console.BackgroundColor = ConsoleColor.Blue
Console.Title = "Medical Practice Data Analysis Application"
Console.Clear()
Console.WriteLine("Please enter the path and name of the file to process:")
inputFile = Console.ReadLine
If (File.Exists(inputFile)) Then
' Call to processData sub if input file exists
processData()
Console.WriteLine(vbCrLf & "Processing Completed...")
Console.WriteLine(vbCrLf & "Please enter the path and name of the report file to generate")
reportFile = Console.ReadLine
File.Create(reportFile).Dispose()
If (File.Exists(reportFile)) Then
Console.WriteLine(vbCrLf & "Report File Generation Completed...")
Else
' Call to sub to end program if directory does not exist
closeProgram()
End If
' Get user input to see report
Console.WriteLine(vbCrLf & "Would you like to see the report file [Y/n]")
yn = Console.ReadLine
' If user inputs "y" or "Y" then print report
' Otherwise close the program
If (yn = "y" OrElse "Y") Then
printFile()
Else
closeProgram()
End If
Else
' Call to sub to end program if file does not exist
closeProgram()
End If
Console.ReadLine()
End Sub
processData Sub():
Public Sub processData()
Dim lines As String() = File.ReadAllLines(inputFile)
Dim tab
Dim dates
Dim diseaseCounter = 0
For Each line As String In lines
tab = line.Split(vbTab)
patientCounter += 1
dates = Date.Parse(line(3))
dateList.Add(dates)
'diseaseList.Add(line(4))
Dim disease As New disease(line(4))
diseaseList.Add(disease.ToString)
'diseaseList(line(4)).
For Each value In diseaseList
'If value.Equals(line(4)) Then disease.counter += 1
Next
Next
Dim uniqueDiseases As String() = diseaseList.Distinct().ToArray
End Sub
Disease.class
Class disease
Dim counter As Integer = 0
Dim name As String = ""
Sub New(newDisease As String)
name = newDisease
counter = 0
End Sub
End Class
printFile()
Sub printFile()
Dim muchoMedical As String = "MuchoMedical Health Center"
Dim diseaseReport As String = "Disease Report For the Period " & "earliest_date" & " through " & "latest_date"
Console.WriteLine(vbCrLf & muchoMedical.PadLeft(Console.WindowWidth / 2))
Console.WriteLine(diseaseReport.PadLeft(Console.WindowWidth / 2))
Console.WriteLine(vbCrLf & "There were a total of " & diseaseList.Count & " unique diseases observed")
Console.WriteLine("A total of " & patientCounter & " patient encounters were held")
Console.WriteLine(vbCrLf & "Relative Histogram of each disease")
For Each disease As String In diseaseList
Console.WriteLine(vbCrLf & disease & vbTab & " ")
Next
End Sub
closeProgram()
Sub closeProgram()
Console.WriteLine(vbCrLf & "File does not exist")
Console.WriteLine("Press Enter to exit the program...")
Console.ReadLine()
End Sub
You don't need a disease class, really, if the most complicated thing you are doing is counting disease occurrences (your disease class had no public members so I don't know what you were doing there anyway). You can simply do everything with a little LINQ.
' processing section
Dim lines = File.ReadAllLines(inputFile)
Dim splitLines = lines.Select(Function(l) l.Split({vbTab}, StringSplitOptions.RemoveEmptyEntries))
Dim diseaseGrouping = splitLines.GroupBy(Function(s) s(3))
Dim patients = splitLines.Select(Function(s) s(1))
Dim dates = splitLines.Select(Function(s) DateTime.Parse(s(2)))
' report section
Dim padAmount = CInt(Console.WindowWidth / 2)
Dim muchoMedical As String = "MuchoMedical Health Center"
Dim diseaseReport As String = $"Disease Report For the Period {dates.Min():d} through {dates.Max():d}"
Console.WriteLine()
Console.WriteLine(muchoMedical.PadLeft(padAmount))
Console.WriteLine(diseaseReport.PadLeft(padAmount))
Console.WriteLine()
Console.WriteLine($"There were a total of {diseaseGrouping.Count()} unique diseases observed.")
Console.WriteLine($"A total of {patients.Count()} patient encounters were held")
For Each diseaseAndCount In diseaseGrouping
Console.WriteLine()
Console.WriteLine($"{diseaseAndCount.Key}{vbTab}{diseaseAndCount.Count()}")
Next
I think your disease name is in index 3. You were looking at 4 originally. Maybe you have a tab between first and last name? Change it if I was wrong. This may apply to any or all of the indices.
Output:
MuchoMedical Health Center
Disease Report For the Period 4/6/2012 through 7/4/2014
There were a total of 4 unique diseases observed.
A total of 5 patient encounters were held
Spastic Colonitis 1
Chronic Phlegm 1
Whooping Cough 1
Scurvy 2
I think the main issue with your code as listed above is that in the processData sub you have:
For Each line As String In lines
tab = line.Split(vbTab)
patientCounter += 1
dates = Date.Parse(line(3))
dateList.Add(dates)
'diseaseList.Add(line(4))
Dim disease As New disease(line(4))
diseaseList.Add(disease.ToString)
'diseaseList(line(4)).
For Each value In diseaseList
'If value.Equals(line(4)) Then disease.counter += 1
Next
Next
I think you more likely mean to use tab(3) and tab(4) instead of line(3) and line(4) etc. You split the line into the "tab" variable but then don't use it. While you could rewrite everything and handle it differently, if you want to go with what you've got, I think that's your core error.
I liked your idea of a class. You can wrap up all your data in one list. I enhanced your class so it could contain all the data in the file. Public Properties are automatic properties that have Get, Set, and the Private fields that hold the data written by the compiler. I have added an Overrides of the .ToString because you were not getting the results you expected. We have the parameterized constructor like you have except expanded to include all the properties.
The magic comes in the Linq query. The d stands for an item in the diseaseList which is an instance of the Disease class. Then I added an order by clause which will produce the results in alphabetical order by DiseaseName which is a string. Grouping by the unique DiseaseName into a Group with Count.
Notice in the second For Each loop we have all the properties of the class available.
I happened to be in a Windows Forms app so I used Debug.Print. Just replace with Console.WriteLine. I leave to you the fancy formatting if you desire.
Public Class Disease
Public Property Name As String
Public Property DiagnosisDate As Date
Public Property DiseaseName As String
Public Property ID As Integer
Public Sub New(PatientID As Integer, PatientName As String, dDate As Date, sDisease As String)
ID = PatientID
Name = PatientName
DiagnosisDate = dDate
DiseaseName = sDisease
End Sub
'If you don't override ToString you will get the fully qualified name of the class
'You can return any combination of the Properties as long as the end
'result is a string
Public Overrides Function ToString() As String
Return Name
End Function
End Class
Public Sub processData()
Dim lines As String() = File.ReadAllLines(inputFile)
Dim diseaseList As New List(Of Disease)
For Each line As String In lines
'I was having trouble with the tabs so I changed it to a comma in the file
'3710079,JUDITH CLOUTIER,2012-08-04,Spastic Colonitis
'the small c following the "," tells the compiler that this is a Char
Dim tab = line.Split(","c)
Dim inputDate = Date.ParseExact(tab(2), "yyyy-MM-dd", CultureInfo.InvariantCulture)
Dim Studentdisease As New Disease(CInt(tab(0)), tab(1), inputDate, tab(3))
diseaseList.Add(Studentdisease)
Next
Dim diseaseGrouping = From d In diseaseList
Order By d.DiseaseName
Group By d.DiseaseName
Into Group, Count
For Each diseaseAndCount In diseaseGrouping
Debug.Print($"{diseaseAndCount.DiseaseName} {diseaseAndCount.Count()} ")
For Each d In diseaseAndCount.Group
Debug.Print($" {d.Name}, {d.DiagnosisDate.ToShortDateString}")
Next
Next
End Sub

How do I count the number of times a word occurs using Visual Basic?

I have just started using visual basic and wanted to create a program that counted the number of times a word appeared. My plan was develop a program that analyses a sentence that contains several words without punctuation. When
a word in that sentence is input, the program identifies all of the positions where the word occurs in the sentence.
I started by making a code that counted the amount of spaces in a sentence but am now stuck.
Module Module1
Sub Main()
Dim Sentence As String
Dim SentenceLength As Integer
Dim Text As String
Console.WriteLine("ASK NOT WHAT YOUR COUNTRY CAN DO FOR YOU ASK WHAT YOU CAN DO FOR YOUR COUNTRY")
Console.WriteLine("Enter your word ") : Sentence = Console.ReadLine
Dim TextCounter As Integer = 0
Dim MainWord As String = Sentence
Dim CountChar As String = " "
Do While InStr(MainWord, CountChar) > 0
MainWord = Mid(MainWord, 1 + InStr(MainWord, CountChar), Len(MainWord))
TextCounter = TextCounter + 1
Text = TextCounter + 2
Console.WriteLine(Text)
Loop
Console.WriteLine(TextCounter)
Console.Write("Press Enter to Exit")
Console.ReadLine()
End Sub
End Module
A quick & dirty method is to split the string into an array of strings, then count how many times a word appears in it:
Dim words() As String = Sentence.Split(new char() {" ", ",", ".", ";"} ' add other punctuation as appropriate
Dim count = words.Count(Function(word) word = MainWord)
This uses the String.Split method to split the string each time a space is encountered. Then it uses the Enumerable.Count extension method to count the words that match a certain condition, that the word is equal to MainWord
To count substrings:
Dim count = UBound(Split("catty cat", "cat")) ' 2
To count words:
Dim countWords = Regex.Matches("catty cat", "\bcat\b").Count ' 1

Read file name for 5 to 10 digit account number, is this possible?

I am attempting to write a program in VB.net which will output some values in to a text file. Please be patient with me as I am very new to VB.net.
What I have so far is below:
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
Dim str As String
For Each File As String In System.IO.Directory.GetFiles(TextBox1.Text)
str = str & File & "|" & System.IO.Path.GetFileNameWithoutExtension(File).Split("-")(0).Trim & "|" & System.IO.Path.GetFileNameWithoutExtension(File).Split("-")(0).Trim & "||" & DateTimePicker1.Text & "|" & Environment.NewLine
Next
System.IO.File.WriteAllText("C:\output\output.txt", str)
End Sub
Results of output file (output.txt) when button3 is clicked are below:
C:\DirectoryTest\Clients\2356851-Kathy Winkler - Family Investments.pdf|2356851|2356851||04/10/2013|
C:\DirectoryTest\Clients\58736 -Katrina Armon - Sandlewood Homes Co.pdf|58736|58736||04/10/2013|
C:\DirectoryTest\Clients\Karen Cooper - 001548 - Famtime.pdf|Karen Cooper|Karen Cooper||04/10/2013|
My code so far does exactly what I want it to do, the only thing is that I want to make the code smarter but don’t know how. Smarter as in is there a way I can make the below code only pick up the 5 to 10 digit account number seen in the filename, and if no account number exists in the file name to bring up a message box?
System.IO.Path.GetFileNameWithoutExtension(File).Split("-")(0).Trim & "|" & System.IO.Path.GetFileNameWithoutExtension(File).Split("-")(0).Trim
As you can see from the last line of the output…
C:\DirectoryTest\Clients\Karen Cooper - 001548 - Famtime.pdf|Karen Cooper|Karen Cooper||04/10/2013|
…the customers name “Karen Cooper” is being displayed in both areas where the account number should be displayed. This is why I need to make this code smarter somehow have it search the file name for a 5 to 10 digit account number to display it after the file name as see in the other 2 examples.
Please let me know if this is possible. And do let me know if you have any questions.
Here is you some simple logic .... ofcouse you can just do something like finding the filename first but here you go
Dim returnval As String = ""
Dim s As String = "C:\DirectoryTest\Clients\Karen Cooper - 001548 - Famtime.pdf|Karen Cooper|Karen Cooper||04/10/2013|"
For Each p As String In s
If IsNumeric(p) Then
returnval += p
Else
'MsgBox("no")
End If
Next
msgbox(returnval) will hold all your numbers 5-10 , depends on how specific you want to get from here
to break apart the filenames
'This will extract and return the filename from the specified path and filename.
'
Dim filePath As String = "c:\MyDirectory\MYFile.txt"
Dim slashPosition As Integer = filePath.LastIndexOf("\")
Dim filenameOnly As String = filePAth.Substring(slashPosition + 1)
MsgBox(filenameOnly)
*FOUND AT LINK http://www.vbforfree.com/274/extract-and-retrieve-the-filename-from-a-path/*
then manipulate your string from there as much as you want
You should give this a try. I haven't had a chance to test it but it should work
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
Dim str As String
For Each File As String In System.IO.Directory.GetFiles(TextBox1.Text)
Dim strs as String() = System.IO.Path.GetFileNameWithoutExtension(File).Split("-")
Dim AccountNum as int = 0
For each section in strs()
' Loop through each section separated by - and try to cast it to an int
' you may want to use cLong instead
Try
AccountNum = cInt(section.trim())
exit for
Catch
End Try
Next
' DO LOGIC HERE TO BUILD OUTPUT with the account num now known
Next
System.IO.File.WriteAllText("C:\output\output.txt", str)
End Sub
How about yor file name ?
C:\DirectoryTest\Clients\Karen Cooper - 001548 - Famtime.pdf
Fairly, It should be
C:\DirectoryTest\Clients\001548 - Karen Cooper - Famtime.pdf
I would recommend using RegEx to extract the account number. A side benefit of using RegEx is that you can store the RegEx pattern outside of your code, such as in a configuration file, so if you ever need to modify the pattern, you could do so easily without recompiling your application.
Function GetAccountNumber(fileName As String) As String
Dim pattern As String = ".*?(?<acct>\d{5,10}).*?"
Dim regEx As New Regex(pattern)
Dim match As Match = regEx.Match(fileName)
Dim accountNumber As String = Nothing
If match.Success Then
Dim group As Group = match.Groups("acct")
If group.Success Then
accountNumber = group.Value
End If
End If
Return accountNumber
End Function
In the above example, I am using the following RegEx pattern to find the five to ten digit number in the string:
.*?(?<acct>\d{5,10}).*?
The .*? at the beginning and end of the pattern means any character, any number of times. The question mark means it's non-greedy. In other words, it only matches as many characters as necessary. By making it non-greedy, it will not steal-away any of the digits from the account number.
The parentheses surround the part of the string we are looking for (the account number). The ?<acct> at the beginning of the parenthetical group a name by which we can refer to it. In this case, I named the group acct. The \d means any digit character. The {5,10} means repeated between five and ten times.

Search string to add line after specific word in VB.net

(I am a VERY basic programmer)
I have a body of text which needs searching through to find a specific word "Customer" and then save the next line as the CustomerName (CustomerName = >line of text<)
If your "body of text" is a TextBox you could take profit of the .Lines property:
Dim index As Integer
Dim customerName As String = ""
For i As Integer = 1 to TextBox1.Lines.Length - 1
If TextBox1.Lines(i-1).Contains("Customer") Then
customerName = TextBox1.Lines(i)
Exit For
End If
Next
If you have a plain text, you can obtain the lines splitting the whole text:
Dim lines() As String = sAllText.Split(Environment.NewLine())
And then doing the same as before, but instead of using TextBox1.Lines, use lines.

Get only the line of text that contains the given word VB2010.net

I have a text file on my website and I download the whole string via webclient.downloadstring.
The text file contains this :
cookies,dishes,candy,(new line)
back,forward,refresh,(new line)
mail,media,mute,
This is just an example it's not the actual string , but it will do for help purposes.
What I want is I want to download the whole string , find the line that contains the word that was entered by the user in a textbox, get that line into a string, then I want to use the string.split with as delimiter the "," and output each word that is in the string into an richtextbox.
Now here is the code that I have used (some fields are removed for privacy reasons).
If TextBox1.TextLength > 0 Then
words = web.DownloadString("webadress here")
If words.Contains(TextBox1.Text) Then
'retrieval code here
Dim length As Integer = TextBox1.TextLength
Dim word As String
word = words.Substring(length + 1) // the plus 1 is for the ","
Dim cred() As String
cred = word.Split(",")
RichTextBox1.Text = "Your word: " + cred(0) + vbCr + "Your other word: " + cred(1)
Else
MsgBox("Sorry, but we could not find the word you have entered", MsgBoxStyle.Critical)
End If
Else
MsgBox("Please fill in an word", MsgBoxStyle.Critical)
End If
Now it works and no errors , but it only works for line 1 and not on line 2 or 3
what am I doing wrong ?
It's because the string words also contains the new line characters that you seem to be omitting in your code. You should first split words with the delimiter \n (or \r\n, depending on the platform), like this:
Dim lines() As String = words.Split("\n")
After that, you have an array of strings, each element representing a single line. Loop it through like this:
For Each line As String In lines
If line.Contains(TextBox1.Text) Then
'retrieval code here
End If
Next
Smi's answer is correct, but since you're using VB you need to split on vbNewLine. \n and \r are for use in C#. I get tripped up by that a lot.
Another way to do this is to use regular expressions. A regular expression match can both find the word you want and return the line that contains it in a single step.
Barely tested sample below. I couldn't quite figure out if your code was doing what you said it should be doing so I improvised based on your description.
Imports System.Text.RegularExpressions
Public Class Form1
Private Sub ButtonFind_Click(sender As System.Object, e As System.EventArgs) Handles ButtonFind.Click
Dim downloadedString As String
downloadedString = "cookies,dishes,candy," _
& vbNewLine & "back,forward,refresh," _
& vbNewLine & "mail,media,mute,"
'Use the regular expression anchor characters (^$) to match a line that contains the given text.
Dim wordToFind As String = TextBox1.Text & "," 'Include the comma that comes after each word to avoid partial matches.
Dim pattern As String = "^.*" & wordToFind & ".*$"
Dim rx As Regex = New Regex(pattern, RegexOptions.Multiline + RegexOptions.IgnoreCase)
Dim M As Match = rx.Match(downloadedString)
'M will either be Match.Empty (no matching word was found),
'or it will be the matching line.
If M IsNot Match.Empty Then
Dim words() As String = M.Value.Split(","c)
RichTextBox1.Clear()
For Each word As String In words
If Not String.IsNullOrEmpty(word) Then
RichTextBox1.AppendText(word & vbNewLine)
End If
Next
Else
RichTextBox1.Text = "No match found."
End If
End Sub
End Class