VB.net Find And Replace from Data in a DataGridView in a text file - vb.net

Im sure someone out there can help, im totally new to coding but getting into it and really enjoying. I know this is such a simple question out there for you folks but i have the following, I load a spread sheet of strings (2 columns) into a datagridview the reason i do this because there is over 100,000 find and replaces and these will generally sit within and existing string when searching, then from there i want to simply search a txt file and find and replace a number of strings in it. So it would check each row in a datagrid take from column 1 the find and use column 2 to replace then outputs the string to another txt file once the find and replace has taken place. My current results are that it just takes what was in the first file and copies without replacing in the second find.
Any assistance is gratefully received, many thanks.
Please see below my amateur code:-
Private Sub CmdBtnTestReplace_Click(sender As System.Object, e As System.EventArgs) Handles CmdBtnTestReplace.Click
Dim fName As String = "c:\backup\logs\masterUser.txt"
Dim wrtFile As String = "c:\backup\logs\masterUserFormatted.txt"
Dim strRead As New System.IO.StreamReader(fName)
Dim strWrite As New System.IO.StreamWriter(wrtFile)
Dim s As String
Dim o As String
For Each row As DataGridViewRow In DataGridView1.Rows
If Not row.IsNewRow Then
Dim Find1 As String = row.Cells(0).Value.ToString
Dim Replace1 As String = row.Cells(1).Value.ToString
Cursor.Current = Cursors.WaitCursor
s = strRead.ReadToEnd()
o = s.Replace(Find1, Replace1)
strWrite.Write(o)
End If
Next
strRead.Close()
strWrite.Close()
Cursor.Current = Cursors.Default
MessageBox.Show("Finished Replacing")
End Sub

1. What you are doing is :
creating a StreamReader whose purpose is to read chars from a File/Stream in sequence.
creating a StreamWriter whose purpose is to add content to a File/Stream.
then looping
a) read the remaining content of file fName and put it in s
b) replace words from s and put the result in o
c) add o to the existing content of the file wrtFile
then the usual closing of the stream reader/writer...
But that doesn't work because, on the secund iteration of the loop, strRead is already at the end of your loaded file, then there is nothing to read anymore, and s is always an empty string starting from the secund iteration.
Furthermore, because s is empty, o will be empty aswell.
And last of all, even if you manage to re-read the content of the file and replace the words, strWrite will not clear the initial content of the output file, but will write the resulting replaced string (o) after the previously updated content of the file.
2. Since you loaded the content of the file in a string (s = strRead.ReadToEnd()), why don't you :
load that s string before the For-Next block
loop the datagridview rows in a For-Next block
replace using the pair Find1/Replace1 s = s.Replace(Find1, Replace1)
then, save the content of s in the targeted file outside the For-Next block
3. However, improving your understanding of how streams work, what should be considered and what are forbidden is a bit outside the scope of SO I think; such documentation could be found/gathered on the MSDN page or with the help of your friend : google. The same applies for finding out/thinking of how you should arrange your code, how to achieve your goal.Let's take an example :
' Content of your file :
One Two Three Four Five Six
' Content of your DataGridView :
One | Two
Two | Three
Three | Four
Four | Five
Five | Six
Six | Seven
The resulting replacement text at the end of a similar routine as yours will be :
Seven Seven Seven Seven Seven Seven ' :/
' while the expected result would be :
Two Three Four Five Six Seven
And that's because of the iteration : already replaced portions of your file (or loaded file content) could get replaced again and again. To avoid that, either :
split the loaded content in single words, and use a "replaced" flag for each word (to avoid replacing that word more than once)
or preload all the pair Find/Replace, and parse the file content in sequence once, replacing that instance when required.
So, before using an interesting object in the framework :
you should know what it does and how it behaves
otherwise -> read the documentation
otherwise -> create a minimalistic test solution which purpose is to brute force testings on that particular object to debunck all its powers and flaws.
So, like I said in 2., move those ReadAllText() and Write() outside the For/Next block to start from and have a look at the resulting output (Ask specific questions in comments when google can't answer) Then if you're OK with it even if issue like the One Two Three example above could occur, then voila ! Otherwise, use google to gather more examples on "splitting text in words" and reformating the whole, have some tries, then get back here if you're stuck on precise issues.

Related

My text file lines sometimes contain the same string in vb.net

I have a text file which contains the same string of characters in different lines. I read the file using this code:
Dim readTexte() As String = File.ReadAllLines(OuvrirFichier, Encoding.UTF8)
Dim t As String
For Each t In readTexte
If t.Contains(TreeView1.SelectedNode.Text) Then
TextBox2.Text = Trim(t.Substring(0, 18))
TextBox1.Text = Trim(t.Substring(18, 90))
TextBox4.Text = Trim(t.Substring(107, 120))
End If
Next
However, I have a problem because when cutting and reading these strings. The code is not able to choose the right line to match the TreeView node, because it identifies several equally perfect strings.
For example, the first line contains 3 substrings (fixed width fields):
Saint, Augustine, and Doctor of the Church.
The second line contains three sub-strings as well, slightly different:
Saint, Monica, and mother of Saint Augustine
When I want to read, my code gives me two Augustines, and mixes Augustine and Monique! The TextBox2 contains the string contained in the treeview.
How can I fix this?
The treeview is created as simply as possible, thus:
Dim readText () As String = File.ReadAllLines (OpenFile,
Encoding.UTF8)
Dim s As String
For Each s In readText
TextBox2.Text = Trim (s.Substring (0, 18))
TextBox1.Text = Trim (s.Substring (18, 90))
TextBox4.Text = Trim (s.Substring (107, 120))
Dim node As TreeNode = Me.TreeView1.Nodes (0)
TreeView1.Nodes (0) .Nodes.Add (New TreeNode (TextBox1.Text))
Next
We need to know more about how the TreeView is built before we can answer this. It's possible there is simply not enough data associated with the TreeView right now, and the solution will be in a completely different area of the code.
However, I can provide some notes. First, reading a file is one of the slowest things possible to do in a computer. We already see this is small enough to fit in memory; if it's also reasonably stable (doesn't change often), you can save significant work by loading to the array once when the program starts.
Next, I wouldn't keep just a simple array of strings. Instead, I'd parse the data into separate fields right at load. A Tuple, Class, or even string array can all work.
Finally, this code will continue looping even after if finds a match. I'd have a way to stop once we find what we're looking for.
Put it all together like this:
'Create a set of Tuples. Could also use a class here.
Dim readTexte() As IEnumerable(Of (String, String, String)) =
File.ReadLines(OuvrirFichier, Encoding.UTF8).
Select(Function(line) (Trim(line.SubString(0, 18)), Trim(line.SubString(18,90)), Trim(line.SubString(107,120))) )
'Search the collection for the first match
Dim result = readTexte.First(Function(record) TreeView1.SelectedNode.Text.Equals(record.Item1))
TextBox2.Text = result.Item1
TextBox1.Text = result.Item2
TextBox4.Text = result.Item3
Again, this doesn't solve your matching problem, because the question doesn't contain the information we need to help do that. Please edit the question to include more details on how the TreeView is created.

how to search and display specific line from a text file vb.net

Hi I am trying to search for a line which contains whats the user inputs in a text box and display the whole line. My code below doesnt display a messsagebox after the button has been clicked and i am not sure if the record has been found
Dim filename, sr As String
filename = My.Application.Info.DirectoryPath + "\" + "mul.txt"
Dim file As String()
Dim i As Integer = 0
file = IO.File.ReadAllLines(filename)
Dim found As Boolean
Dim linecontain As Char
sr = txtsr.ToString
For Each line As String In file
If line.Contains(sr) Then
found = True
Exit For
End If
i += 1
If found = True Then
MsgBox(line(i))
End If
Next
End Sub
You should be calling ReadLines here rather than ReadAllLines. The difference is that ReadAllLines reads the entire file contents into an array first, before you can start processing any of it, while ReadLines doesn't read a line until you have processed the previous one. ReadAllLines is good if you want random access to the whole file or you want to process the data multiple times. ReadLines is good if you want to stop processing data when a line satisfies some criterion. If you're looking for a line that contains some text and you have a file with one million lines where the first line matches, ReadAllLines would read all one millions lines whereas ReadLines would only read the first.
So, here's how you display the first line that contains specific text:
For Each line In File.ReadLines(filePath)
If line.Contains(substring) Then
MessageBox.Show(line)
Exit For
End If
Next
With regards to your original code, your use of i makes no sense. You seem to be using i as a line counter but there's no point because you're using a For Each loop so line contains the line. If you already have the line, why would you need to get the line by index? Also, when you try to display the message, you are using i to index line, which means that you're going to get a single character from the line rather than a single line from the array. If the index of the line is greater than the number of characters in the line then that is going to throw an IndexOutOfRangeException, which I'm guessing is what's happening to you.
This is what comes from writing code without knowing what it actually has to do first. If you had written out an algorithm before writing the code, it would have been obvious that the code didn't implement the algorithm. If you have no algorithm though, you have nothing to compare your code to to make sure that it makes sense.

VB.Net Read multi column text file and load into ListBox

First, I am not a programmer, I mainly just do simple scripts however there are somethings that are just easier to do in VB, I am pretty much self taught so forgive me if this sounds basic or if I can't explain it to well.
I have run into an issue trying to load a multi-column text file into a list box. There are two separate issues.
First issue is to read the text file and only grab the first column to use in the listbox, I am currently using ReadAllLines to copy the text file to a string first.
Dim RDPItems() As String = IO.File.ReadAllLines(MyDocsDir & "\RDPservers.txt")
However I am having a difficult time finding the correct code to only grab the first Column of this string to put in the listbox, if I use the split option I get an error that "Value of type '1-dimensional array of String' cannot be converted to 'String'"
The code looked like
frmRDP.lstRDP.Items.Add() = Split(RDPItems, ";", CompareMethod.Text)
This is the first hurdle, the second issue is what I want to do is if an item is selected from the List box, the value of the second column gets pulled into a variable to use.
This part I'm not even sure where to begin.
Example data of the text file
Server1 ; 10.1.1.1:3389
Server2 ; 192.168.1.1:8080
Server3 ; 172.16.0.1:9833
.....
When it's working the application will read a text file with a list of servers and their IPs and put the servers in a listbox, when you select the server from the listbox it and click a connect button it will then launch
c:\windows\system32\mstsc.exe /v:serverip
Any help would be appreciated, as I can hard code a large list of this into the VB application it would be easier to just have a text file with a list of servers and IPs to load instead.
The best practise for this would probably be to store your "columns" in a Dictionary. Declare this at class level (that is, outside any Sub or Function):
Dim Servers As New Dictionary(Of String, String)
When you load your items you read the file line-by-line, adding the items to the Dictionary and the ListBox at the same time:
Using Reader As New IO.StreamReader(IO.Path.Combine(MyDocsDir, "RDPservers.txt")) 'Open the file.
While Reader.EndOfStream = False 'Loop until the StreamReader has read the whole file.
Dim Line As String = Reader.ReadLine() 'Read a line.
Dim LineParts() As String = Line.Split(New String() {" ; "}, StringSplitOptions.None) 'Split the line into two parts.
Servers.Add(LineParts(0), LineParts(1)) 'Add them to the Dictionary. LineParts(0) is the name, LineParts(1) is the IP-address.
lstRDP.Items.Add(LineParts(0)) 'Add the name to the ListBox.
End While
End Using 'Dispose the StreamReader.
(Note that I used IO.Path.Combine() instead of simply concatenating the strings. I recommend using that instead for joining paths together)
Now, whenever you want to get the IP-address from the selected item you can just do for example:
Dim IP As String = Servers(lstRDP.SelectedItem.ToString())
Hope this helps!
EDIT:
Missed that you wanted to start a process with it... But it's like charliefox2 wrote:
Process.Start("c:\windows\system32\mstsc.exe", "/v:" & Servers(lstRDP.SelectedItem.ToString()))
Edit: #Visual Vincent's answer is way cleaner. I'll leave mine, but I recommend using his solution instead. That said, scroll down a little for how to open the server. He's got that too! Upvote his answer, and mark it as correct!
It looks like you're trying to split an array. Also, ListBox.Items.Add() works a bit differently than the way you've written your code. Let's take a look.
ListBox.Items.Add() requires that you provide it with a string inside the parameters. So you would do it like this:
frmRDP.lstRDP.Items.Add(Split(RDPItems, ";", CompareMethod.Text))
But don't do that!
When you call Split(), you must supply it with a string, not an array. In this case, RDPItems is an array, so we can't split the entire thing at once. This is the source of the error you were getting. Instead, we'll have to do it one item at a time. For this, we can use a For Each loop. See here for more info if you're not familiar with the concept.
A For Each loop will execute a block of code for each item in a collection. Using this, we get:
For Each item In RDPItems
Dim splitline() As String = Split(item, ";") 'splits the item by semicolon, and puts each portion into the array
frmRDP.lstRDP.Items.Add(splitline(0)) 'adds the first item in the array
Next
OK, so that gets us our server list put in our ListBox. But now, we want to open the server that our user has selected. To do that, we'll need an event handler (to know when the user has double clicked something), we'll have to find out which server they selected, and then we'll have to open that server.
We'll start by handling the double click by creating a sub to deal with it:
Private Sub lstRDP_MouseDoubleClick(sender As Object, e As MouseEventArgs) Handles lstRDP.MouseDoubleClick
Next, we'll get what the user has selected. Here, we're setting selection equal to the index that the user has selected (in this case, the first item is 0, the second is 1, and so on).
Dim selection As Integer = lstRDP.SelectedIndex
Lastly, we need to open the server. I'm assuming you want to do that in windows explorer, but if I'm mistaken please let me know.
Dim splitline() As String = Split(RDPItems(selection), ";")
Dim location As String = Trim(splitline(1))
We'll need to split the string again, but you'll notice this time I'm choosing the item whose location in the array is the same as the index of the list box the user has selected. Since we added our items to our listbox in the order they were added to our array, the first item in our listbox will be the first in the array, and so on. The location of the server will be the second part of the split function, or splitline(1). I've also included the Trim() function, which will remove any leading or trailing spaces.
Finally, we need to connect to our server. We'll use Process.Start() to launch the process.
Process.Start("c:\windows\system32\mstsc.exe", "/v:" & location)
For future reference, to first argument for Process.Start() is the location of the process, and the second argument is any argument the process might take (in this case, what to connect to).
Our final double click event handler looks something like this:
Private Sub lstRDP_MouseDoubleClick(sender As Object, e As MouseEventArgs) Handles lstRDP.MouseDoubleClick
Dim selection As Integer = lstRDP.SelectedIndex
Dim splitline() As String = Split(RDPItems(selection), ";")
Dim location As String = Trim(splitline(1))
Process.Start("c:\windows\system32\mstsc.exe", "/v:" & location)
End Sub
A final note: You may need to put
Dim RDPItems() As String = IO.File.ReadAllLines(MyDocsDir & "\RDPservers.txt")
outside of a sub, and instead just inside your class. This will ensure that both the click handler and your other sub where you populate the list box can both read from it.

What am I doing wrong when splitting lines from a textfile

I am working on a quiz for my computer science program and the idea is having keywords and definitions. The User will be shown a keyword that gets randomized and three definitions in radiobuttons, one must be the correct answer.
I decided to put my keywords and my defintions in the same file like so:
Keyword1 = Definition1
Keyword2 = Definition2
Keyword3 = Definition3
etc.
On my main form I have the following loop:
For Each line As String In System.IO.File.ReadAllLines("my-file-path-here")
Dim Pair() As String = line.split("=")
LabelKeyword.text = Trim(Pair(0)) ' Needs Randomizing
RadioButtonDef1.text = Trim(Pair(1)) ' Needs randomizing
'RadioButtonDef2.text = 'Don't know needs randomizing
'RadioButtonDef3.text = 'Don't know needs randomizing
Next
What I can't seem to figure out is how to randomize the keywords and the definitions. Also I have 15 lines in my text file and it always seems to only read the last line.
So my questions are:
How can I randomize the keywords and the definitions always having one of the definitions the answer to the current keyword.
Why does it only read the very last line of my text file and how can I fix it?
I also need to be able to make the quiz only finish when every keyword has been matched to its definition twice. Their time taken also needs to be recored as they play.
You are looping through all of the lines in your text file, but you are displaying all of them in the same control. Therefore, each time you iterate through the loop, it overwrites the values in the controls from the last iteration. I would strongly suggest storing your values in some sort of data-structure in memory. Then you can access the values randomly at will. For instance, you could store all the values in a Dictionary, like this:
Dim dict As New Dictionary(Of String, String)()
For Each line As String In File.ReadAllLines("...")
Dim pair() As String = line.Split("=")
dict(pair(0)) = pair(1)
Next

How can I read individual lines of a CSV file into a string array, to then be selectively displayed via combobox input?

I need your help, guys! :|
I've got myself a CSV file with the following contents:
1,The Compact,1.8GHz,1024MB,160GB,440
2,The Medium,2.4GHz,1024MB,180GB,500
3,The Workhorse,2.4GHz,2048MB,220GB,650
It's a list of computer systems, basically, that the user can purchase.
I need to read this file, line-by-line, into an array. Let's call this array csvline().
The first line of the text file would stored in csvline(0). Line two would be stored in csvline(1). And so on. (I've started with zero because that's where VB starts its arrays). A drop-down list would then enable the user to select 1, 2 or 3 (or however many lines/systems are stored in the file). Upon selecting a number - say, 1 - csvline(0) would be displayed inside a textbox (textbox1, let's say). If 2 was selected, csvline(1) would be displayed, and so on.
It's not the formatting I need help with, though; that's the easy part. I just need someone to help teach me how to read a CSV file line-by-line, putting each line into a string array - csvlines(count) - then increment count by one so that the next line is read into another slot.
So far, I've been able to paste the numbers of each system into an combobox:
Using csvfileparser As New Microsoft.VisualBasic.FileIO.TextFieldParser _
("F:\folder\programname\programname\bin\Debug\systems.csv")
Dim csvalue As String()
csvfileparser.TextFieldType = Microsoft.VisualBasic.FileIO.FieldType.Delimited
csvfileparser.Delimiters = New String() {","}
While Not csvfileparser.EndOfData
csvalue = csvfileparser.ReadFields()
combobox1.Items.Add(String.Format("{1}{0}", _
Environment.NewLine, _
csvalue(0)))
End While
End Using
But this only selects individual values. I need to figure out how selecting one of these numbers in the combobox can trigger textbox1 to be appended with just that line (I can handle the formatting, using the string.format stuff). If I try to do this using csvalue = csvtranslator.ReadLine , I get the following error message:
"Error 1 Value of type 'String' cannot be converted to '1-dimensional array of String'."
If I then put it as an array, ie: csvalue() = csvtranslator.ReadLine , I then get a different error message:
"Error 1 Number of indices is less than the number of dimensions of the indexed array."
What's the knack, guys? I've spent hours trying to figure this out.
Please go easy on me - and keep any responses ultra-simple for my newbie brain - I'm very new to all this programming malarkey and just starting out! :)
Structure systemstructure
Dim number As Byte
Dim name As String
Dim procspeed As String
Dim ram As String
Dim harddrive As String
Dim price As Integer
End Structure
Private Sub csvmanagement()
Dim systemspecs As New systemstructure
Using csvparser As New FileIO.TextFieldParser _
("F:\folder\programname\programname\bin\Debug\systems.csv")
Dim csvalue As String()
csvparser.TextFieldType = Microsoft.VisualBasic.FileIO.FieldType.Delimited
csvparser.Delimiters = New String() {","}
csvalue = csvparser.ReadFields()
systemspecs.number = csvalue(0)
systemspecs.name = csvalue(1)
systemspecs.procspeed = csvalue(2)
systemspecs.ram = csvalue(3)
systemspecs.harddrive = csvalue(4)
systemspecs.optical = csvalue(5)
systemspecs.graphics = csvalue(6)
systemspecs.audio = csvalue(7)
systemspecs.monitor = csvalue(8)
systemspecs.software = csvalue(9)
systemspecs.price = csvalue(10)
While Not csvparser.EndOfData
csvalue = csvparser.ReadFields()
systemlist.Items.Add(systemspecs)
End While
End Using
End Sub
Edit:
Thanks for your help guys, I've managed to solve the problem now.
It was merely a matter calling loops at the right point in time.
I would recommend using FileHelpers to do the reading.
The binding shouldn't be an issue after that.
Here is the Quickstart for Delimited Records:
Dim engine As New FileHelperEngine(GetType( Customer))
// To Read Use:
Dim res As Customer() = DirectCast(engine.ReadFile("FileIn.txt"), Customer())
// To Write Use:
engine.WriteFile("FileOut.txt", res)
When you get the file read, put it into a normal class and just bind to the class or use the list of items you have to do custom stuff with the combobox. Basically, get it out of the file and into a real class asap, then things will be easier.
At least take a look at the library. After using it, we use a lot more simple flat files since it is so easy, and we haven't written a file access routine since (for that kinda stuff).
http://msdn.microsoft.com/en-us/library/microsoft.visualbasic.fileio.textfieldparser.aspx
I think your main problem is understanding how arrays work (hence the error message).
You can use split and join functions to convert strings into and out of arrays
dim s() as string = split("1,2,3",",") gives and array of strings with 3 elements
dim ss as string = join(s,",") gives you the string back
Firstly, it's actually really good that you are using the TextFieldParser for reading CSV files - most don't but you won't have to worry about extra commas and quoted text etc...
The Readline method only gives you the raw string, hence the "Error 1 Value of type 'String' cannot be converted to '1-dimensional array of String'."
What you may find easier with combo boxes etc is to use an object (e.g. 'systemspecs') rather than strings. Assign the CSV data to the objects and override the "ToString" method of the 'systemspecs' class to display in the combo box how you want with formatting etc. That way when you handle the SelectedIndexChanged event (or similar) you get the "SelectedItem" from the combo box (which can be Nothing so check) and cast it as the 'systemspecs' to use it. The advantage is that you are not restricted to display the exact data in the combo etc.
' in "systemspecs"...
Public Overrides Function ToString() As String
Return Name ' or whatever...
End Function ' ToString
e.g.
dim item as new systemspecs
item.ID = csvalue(1)
item.Name = csvalue(2)
' etc...
combobox1.Items.Add(item)
Let me know if that makes sense!
PK :-)