Overwrite a specific line in a text file using VB.NET - vb.net

I need to do the following:
Change the line in a text file
[Path] = "c:\this\certain\path\"
with this line
[Path] = "c:\that\other\newer\path\"
These paths will most certainly be different lengths, so I need to either replace what's in the quotes or erase the line completely and enter a new one, but in the same spot, not appended to the end of the document.

This will do the trick
Dim thefile As String = "filepath"
Dim lines() As String = System.IO.File.ReadAllLines("filepath")
lines(number of line you want to replace) = "write what you want to replace here"
System.IO.File.WriteAllLines(filepath, lines)

If you really know exactly how the line you want to replace looks and the file you're reading isn't really big, you could try to just use Replace() to add the new line instead of the old one:
Dim reader As New StreamReader("foo.txt")
Dim writer As New StreamWriter("output.txt")
Dim s = reader.ReadToEnd().Replace("[Path]: C:\oldPath\file.txt", "[Path]: C:\newPath")
writer.Write(s)

One quick way is to use readAllLines and WriteAllLines:
Dim ss() As String
ss = File.ReadAllLines([path])
ss(47) = "c:\that\other\newer\path\"
File.WriteAllLines([path], ss)
If you don't know which line to change, you can search through the array ss for it.

Read the text file into a string, iterate over each line and check if it's in the format:
[Path] = "...." (with regular expressions or simply with string.StartsWith("[Path] = "))
In this loop you should be writing out all other lines and when you are on this [Path] line, print out the modified one.
So in code (sorry, it is in C#):
var reader = File.OpenText("foo.txt");
var writer = new StreamWriter("output.txt");
string line;
while ((line=reader.ReadLine()) != null)
{
if (line.StartsWith("[Path]"))
writer.WriteLine("[Path] = \"c:\\that\\other\\newer\\path\\\"");
else
writer.WriteLine(line);
}
of course, close and dispose the StreamReader and StreamWriter.

Here's the deal: due to the way files are stored on disk, you can't write to one line without also updating every line that follows it.
There are number of ways to do this, and the one most appropriate for your situation will depend on things like the size of the file, are you doing this to a lot of files, where in the file you expect to find this, etc.
But most of the time what I like to do is actually create a copy of the old file... So as I seek through the file looking for the line(s) I need to change, I'm also writing what I've read to a new location. When I find the line, I write out the new information. I then keep seeking through the file until I reach the end at which time I close both streams, delete the original, and rename the new one.

First build a function to give the value of line 'n':
Public Function daValorConfig(ByVal numValor As Long, ByVal nomeFicheiroINI As String) As String
Dim reader As StreamReader = New StreamReader(Application.StartupPath & "\" & nomeFicheiroINI & ".ini")
Dim valor As String = ""
daValorConfig = ""
Dim i As Long = 1
Try
While i <= numValor
valor = reader.ReadLine()
i = i + 1
End While
daValorConfig = valor
reader.Close()
Catch ex As Exception
reader.Close()
MessageBox.Show(ex.Message, "Error: ", MessageBoxButtons.OK, MessageBoxIcon.Error)
Err.Clear()
End Try
End Function
Then build a procedure that writes the new value on the specified line or keep the old one if the line is not the one you specify:
Public Sub guardaValorConfig(ByVal dados As String, ByVal numValor As Long, ByVal nomeFicheiroINI As String)
Dim writer As StreamWriter = New StreamWriter(Application.StartupPath & "\" & nomeFicheiroINI & ".ini")
Dim valor As String = ""
Dim i As Long = 1
Try
While i <= numValor
If i = numValor Then
writer.Write(dados)
Else
writer.Write(daValorConfig(i, nomeFicheiroINI))
End If
i = i + 1
End While
writer.Close()
Catch ex As Exception
MessageBox.Show(ex.Message, "Error: ", MessageBoxButtons.OK, MessageBoxIcon.Error)
Err.Clear()
End Try
End Sub

Related

Text file split in blocks vb.net

I am trying to go through my text file and create a new file that will contain only the text I require. My current line looks like:
Car-1I
Colour-39
Cost-328
Dealer-28
Car-2
Colour-30
Cost-234
For each block of text I would like to read the first line, if the first line ends with an I, then read the next line, if that line contains a colour 39, then I would like to save the whole block of text to another file. If these two conditions aren't met, I dont want to save my values to the new text file.
Before anything about saving my values in classes are mentioned, these blocks of text can vary in size and values, so I dont always have a set range of values which is why i need to skip to the blank line
IO.File.WriteAllText("C:\Users\test2.txt", "") 'write to new file
Dim sKey As String
Dim sValue As Integer
For Each filterLine As String In File.ReadLines("C:\Users\test.txt")
sKey = Split(filterLine, ":")(0)
sValue = Split(filterLine, ":")(1)
If Not sValue.EndsWith("I") Then
ElseIf sValue.EndsWith("I") Then
End If
Next
Another method, using File.ReadLines to read lines of text from file. This method doesn't load all the text in memory, it reads from disc single lines of text, so it can also be useful when dealing with big files.
You could loop the IEnumerable collection it returns, but also use its GetEnumerator() method to control more directly when to move to the next line, or move more then one lines forward.
Its Enumerator.Current object returns the line of text currently read, Enumerator.MoveNext() moves to the next line.
A StringBuilder is used to store the strings when a match found. Strings are added to the StringBuilder object using its AppendLine() method.
This class is useful when dealing with strings that you need to store, compare and discard (or modify) quickly: since string are immutable, when you use String variables directly, especially in loops, you generate a whole lot of garbage that slows down any procedure quite a lot.
The blocks of text stored in the StringBuilder object are then written to a destination file using a StreamWriter with explicit encoding set to UTF-8 (writes the BOM). Its methods include asynchronous versions: WriteLine() can be replaced by awaitWriteLineAsync() to allow an async procedure.
Imports System.IO
Imports System.Text
Dim sourceFilePath = "<Path of the source file>"
Dim resultsFilePath = "<Path of the destination file>"
Dim sb As New StringBuilder()
Dim enumerator = File.ReadLines(sourceFilePath).GetEnumerator()
Using sWriter As New StreamWriter(resultsFilePath, False, Encoding.UTF8)
While enumerator.MoveNext()
If enumerator.Current.EndsWith("I") Then
sb.AppendLine(enumerator.Current)
enumerator.MoveNext()
If enumerator.Current.EndsWith("39") Then
While Not String.IsNullOrWhiteSpace(enumerator.Current)
sb.AppendLine(enumerator.Current)
enumerator.MoveNext()
End While
sWriter.WriteLine(sb.ToString())
End If
sb.Clear()
End If
End While
End Using
This will work:
Dim strFile As String = "c:\Test5\Source.txt"
Dim strOutFile As String = "c:\Test5\OutPut.txt"
Dim strOutData As String = ""
Dim SourceGroups As String() = Split(File.ReadAllText(strFile), vbCrLf + vbCrLf)
For Each sGroup As String In SourceGroups
Dim OneGroup() As String = Split(sGroup, vbCrLf)
If Strings.Right(OneGroup(0), 1) = "I" And (Strings.Right(OneGroup(1), 2) = "39") Then
If strOutData <> "" Then strOutData += (vbCrLf & vbCrLf)
strOutData += sGroup
End If
Next
File.WriteAllText(strOutFile, strOutData)
Something like this should work:
Dim base, i, c as Integer
Dim lines1$() = File.ReadLines("C:\Users\test.txt")
c = lines1.count
While i < c
if Len(RTrim(lines1(i))) Then
If Strings.Right(RTrim(lines1(i)), 1)="I" Then
base = i
i += 1
If Strings.Right(RTrim(lines1(i)), 2)="39" Then
While Len(RTrim(lines1(i))) 'skip to the next blank
i += 1
End While
' write lines1(from base to (i-1)) here
Else
While Len(RTrim(lines1(i)))
i += 1
End While
End If
Else
i += 1
End If
Else
i += 1
End If
End While

Count words in an external file using delimiter of a space

I want to calculate the number of words in a text file using a delimiter of a space (" "), however I am struggling.
Dim counter = 0
Dim delim = " "
Dim fields() As String
fields = Nothing
Dim line As String
line = Input
While (SR.EndOfStream)
line = SR.ReadLine()
End While
Console.WriteLine(vbLf & "Reading File.. ")
fields = line.Split(delim.ToCharArray())
For i = 0 To fields.Length
counter = counter + 1
Next
SR.Close()
Console.WriteLine(vbLf & "The word count is {0}", counter)
I do not know how to open the file and to get the do this, very confused; would like an explanation so I can edit and understand from it.
You're going to be reading a file as the source of the data, so let's create a variable to refer to its filename:
Dim srcFile = "C:\temp\twolines.txt"
As you have shown already, a variable is needed to hold the number of words found:
Dim counter = 0
To read from the file, a StreamReader will do the job. Now, we look at the documenation for it (yes, really) and notice that it has a Dispose method. That means that we have to explicitly dispose of it after we've used it to make sure that no system resources are tied up until the computer is next rebooted (e.g there could be a "memory leak"). Fortunately, there is the Using construct to take care of that for us:
Using sr As New StreamReader(srcFile)
And now we want to iterate over the content of the file line-by-line until the end of the file:
While Not sr.EndOfStream
Then we want to read a line and find how many items separated by spaces it has:
counter += sr.ReadLine().Split({" "c}, StringSplitOptions.RemoveEmptyEntries).Length
The += operator is like saying "add n to a" instead of saying "a = a + n". The {" "c} is a literal array of the character " "c. The c tells it that is a character and not a string of one character. The StringSplitOptions.RemoveEmptyEntries means that if there was text of "one two" then it would ignore the extra spaces.
So, if you were writing a console program, it might look like:
Imports System.IO
Module Module1
Sub Main()
Dim srcFile = "C:\temp\twolines.txt"
Dim counter = 0
Using sr As New StreamReader(srcFile)
While Not sr.EndOfStream
counter += sr.ReadLine().Split({" "c}, StringSplitOptions.RemoveEmptyEntries).Length
End While
End Using
Console.WriteLine(counter)
Console.ReadLine()
End Sub
End Module
Any embellishments such as writing out what the number represents or error checking are left up to you.
With Path.Combine you don't have to worry about where the slashes or back slashes go. You can get the path of special folders easily using the Environment class. The File class of System.IO is shared so you don't have to create an instance.
Public Sub Main()
Dim p As String = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "Chapters.txt")
Debug.Print(Environment.SpecialFolder.MyDocuments.ToString)
Dim count As Integer = GetCount(p)
Console.WriteLine(count)
Console.ReadKey()
End Sub
Private Function GetCount(Path As String) As Integer
Dim s = File.ReadAllText(Path)
Return s.Split().Length
End Function
Use Split function, then Directly get the length of result array and add 1 to it.

In Vb.Net,How to write a .csv file without Carriage Return?

I am working on VS 2012, Vb.Net - .Net 4.0 framework.
My Vb.Net code is reading the .csv file and it is expected to rewrite the file without Carriage Return.
But now, Carriage Return Line Feed is created as a new line in the .csv file.
How to delete CR LF ??
Public Sub Test()
Try
Dim reader As StreamReader = New System.IO.StreamReader(File.OpenRead("D:\CSV\Test.csv"))
Dim listA As New List(Of String)()
If File.Exists("d:\CSV\TestOut.csv") Then
File.Delete("d:\CSV\TestOut.csv")
End If
Dim sw As New StreamWriter("d:\CSV\TestOut.csv")
Dim s As String = String.Empty
While reader.Peek() >= 0
Dim line As String = reader.ReadLine()
Dim values As String() = line.Split(";"c)
listA.Add(values(0))
s = s + line + Chr(10)
End While
reader.Close()
sw.WriteLine(s)
sw.Close()
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
End Sub
I don't see what the listA or the values variables are good for. You don't need them for the CRLF to LF task.
See:
Try
If File.Exists("D:\CSV\Output.csv") Then File.Delete("D:\CSV\Output.csv")
Using reader As New StreamReader(File.OpenRead("D:\CSV\Input.csv"))
Using writer As New StreamWriter("D:\CSV\Output.csv")
Dim strBldr As New Text.StringBuilder
While reader.Peek > -1 ' Seen like this at MSDN page for Peek() function
Dim line = reader.ReadLine
' If you really need the first value of each line, you can still do it here.
strBldr.Append(line).Append(Chr(10))
End While
writer.Write(strBldr.ToString)
End Using
End Using
Catch ex As Exception
MsgBox(ex.Message)
End Try
You should use the Using-Keyword for Streams. When leaving, End Using will close and Dispose() the stream object. It will always do, even if exceptions are thrown or you return a value; just like Finally statements in a Try block.
When you expand a String-Variable often at the end like s = s + line + Chr(10), please consider using a StringBuilder which is optimized for this.
When you need the first and only the first value in the list, line.Split({";"c}, 2) will split the first value and leaves the big rest as one String and not maybe hundreds of String objects just for the garbage collection. Even more appropriate would be using line.Substring(0, line.IndexOf(";")) which doesn't generate a rest at all.

My List(OF Strings) are being saved as system.string (Empty)

I'm trying to delete a selected row, then save the rest into a file. However, when I save it, it totally empties the file.
Console.Write("Please eneter the first name of the student you wish to search for: ")
searchfname = Console.ReadLine
searchfname = StrConv(searchfname, VbStrConv.ProperCase)
Console.Write("Please enter the second name of the student you wish to search for: ")
searchsname = Console.ReadLine
searchsname = StrConv(searchsname, VbStrConv.ProperCase)
Dim foundItem() As String = Nothing
Dim foundline As String = Nothing
Dim fnsearch As String = String.Join(searchfname, searchsname)
Dim lines As New List(Of String)(File.ReadAllLines("F:\Computing\Spelling Bee\stdnt&staffdtls.csv"))
For Each line As String In lines
If searchfname = item(3) And searchsname = item(4) Then
Console.WriteLine(line)
Console.WriteLine()
Console.WriteLine("Are you sure you wish to delete this record? (y/n)")
End If
Dim answer As String
answer = Console.ReadLine
If answer = "y" Or answer = "Y" Then
Console.Clear()
lines.Remove(line)
Using sw As New StreamWriter("F:\Computing\Spelling Bee\stdnt&staffdtls.csv")
sw.WriteLine(lines.ToString)
End Using
ElseIf answer = "n" Or answer = "N" Then
staffmenu()
End If
Next
Look at this line in your code:
sw.WriteLine(lines.ToString)
Extract the lines.ToString expression from that statement. The result of that expression is "System.String". You are telling your stream writer to write the text "System.String" to the file.
To fix it, you need something more like this:
Using sw As New StreamWriter("F:\Computing\Spelling Bee\stdnt&staffdtls.csv")
For Each line As String In lines
sw.WriteLine(line)
Next line
End Using
The method List(Of T).ToString does not produce a value that includes the elements of the collection. Instead it will just return the type name.
The API you are looking for is File.WriteAllLines. Using this instead of StreamWriter and the Using block
File.WriteAllLines("F:\Computing\Spelling Bee\stdnt&staffdtls.csv", lines)
I can see that this issue can be resolved from the given answers and comment, but I would like to add an alternative to use Join function in writing to a file. Try like this may be of help:
Using sw As New StreamWriter(.....)
sw.WriteLine(Join(lines.ToArray(), Environment.NewLine))
End Using
Since using VB.Net, this is a vb.net specific solution can not be used in C#. For C#, use string.join instead.
Hope it helps too!

VB.Net editing a CSV (or delimited file)

I would like to know a way (of course I want to know the best way) to edit a csv file, preferably without having to read the original and write the results to a new one.
Example 1: I want to split the contents of a column into two columns 123abc into 123, abc
Example 2: I want to remove letters from the beginning of a column AB123 into 123
Clarification: I do not need help solving my examples; I just need to be pointed in the right direction of reading and editing the same file.
Here is a code example of using a temporary file that seems to me is too slow
Dim currentLine as string()
tempFile.AutoFlush = True
Do Until origFile.EndOfData
currentLine = origFile.ReadFields
currentLine(1) = currentLine(1).Replace("ABC","") 'would convert ABC123 to 123
For index as Integer = 0 to currentLine.Count - 2
tempFile.Write(currentLine(index) & ",")
Next
tempFile.Write(currentLine(currentLine.Count - 1))
tempFile.WriteLine()
Loop
tempFile.Flush()
tempFile.Dispose()
origFile.Dispose()
IO.File.Copy(tempFile,OrigFile,True)
IO.File.Delete(tempFile)
You really will be best off writing a new file. You can write to a temporary file, delete the old file and rename the new file as the old file.
Something like:
Imports System.IO
Module Module1
Sub Main()
Dim tempfile = Path.GetTempFileName()
Dim inputFile = "C:\Temp\input.txt"
Using sw = New StreamWriter(tempfile)
Using MyReader As New Microsoft.VisualBasic.FileIO.TextFieldParser(inputFile)
MyReader.TextFieldType = Microsoft.VisualBasic.FileIO.FieldType.Delimited
MyReader.Delimiters = New String() {","}
Dim currentRow As String()
While Not MyReader.EndOfData
Try
currentRow = MyReader.ReadFields()
If currentRow.Count >= 2 Then
currentRow(1) = currentRow(1).Replace("ABC", "")
End If
sw.WriteLine(String.Join(",", currentRow))
Catch ex As Microsoft.VisualBasic.FileIO.MalformedLineException
MsgBox("Line " & ex.Message & " is invalid. Skipping")
End Try
End While
End Using
End Using
File.Delete(inputFile)
File.Move(tempfile, inputFile)
End Sub
End Module
(Basically copying the TextFieldParser example from http://msdn.microsoft.com/en-us/library/microsoft.visualbasic.fileio.textfieldparser.readfields.aspx )
You can use ADO to connect to the CSV file. See here for more info:
How to read a CSV file into a .NET Datatable
However, I would recommend simply loading it into an array and then writing it back out with simple file IO methods. It's just a lot less code and overhead:
For Each row As String In File.ReadAllLines("C:\test.csv")
For Each column As String In row.Split(New String() {","}, StringSplitOptions.None)
' Do work
Next
Next
And then something like this to save the file:
File.WriteAllLines(rows)