In this Part 1 of my tutorial series on working with the System.IO Namespace with VB.Net, we will take a look at working with the
System.IO Namespace in VB.Net to do various manipulations on text files, then in Part II we'll get into file conversions, such as converting a delimited file into an XML Document and so on..
- TextReader
- TextWriter
- StreamReader
All of these are members of the
System.IO Namespace, which contains objects and classes for reading from and writing to data streams, they also provide file and directory functionality.
A lot of this code will look similar to the tutorial I wrote on the same topic in C#, but I have optimized the majority of the code (I do that a lot when reading code Ive already written), and have introduced working with
Structures in VB.Net. Throwing this into the mis has allowed me to remove 3 functions from the class, thus optimizing it even further.
In this tutorial we will start with the basic file and directory operations such as reading from a text file, writing to a
text file, and checking if a file exists before working with it. In part II we will then look at more intermediate processes such as creating a directory, converting a comma delimited file to an XML document, copying and deleting both a single file and all the files in a directory, and how to access file Properties such as ReadOnly status and Last Access time.
The first thing we need to add to our class, as with any class we write, are the Namespaces we will need. Once new one from the C# version of this Class Library is using the
System.Collections.Generic Namespace, as we will be introducing working with a Generic list that is of the type of our structure we'll be looking at later. First the Namespaces:
CODE
Imports System
Imports System.IO
Imports System.Data.SqlClient
Imports System.Collections
Imports System.Collections.Generic
Imports System.Collections.ObjectModel
Earlier I mentiond the addition of a
Structure, which is like a class, but is normally used for grouping like data members (types of cars, file attributes, etc). The Structure Ive introduced allowed me to remove all the Functions used to retrieve certain file attributes, now I can do it in a single function and return the results in a Generic List(Of T):
CODE
#Region " Structures "
''' <summary>
''' Structure to hold various file attributes
''' </summary>
''' <remarks></remarks>
Structure FileInformation
Public ReadOnlyStatus As Boolean
Public HiddenStatus As Boolean
Public FileSize As Double
Public LastAccess As DateTime
Public FileCreatedOn As DateTime
Public LastWrite As DateTime
Public Name As String
Public FileExtension As String
Public FileLocation As String
End Structure
#End Region
Another thing I did to try and improve the performance and scalability of this Class Library is by introducing
Properties, thus allowing the end user to not have to pass all information in the signature of a method (such as file name, directory, etc). As you know, Properties need private variables to populate them, making them private means their values cannot be altered outside the class. So now for the Properties for this Class Library:
CODE
#Region " Variables "
Dim _fileName As String
Dim _directoryName As String
Dim _conversionValue As Integer
Dim _conversionType As String
Dim _fileSize As Double
Dim _textToWrite As String
Dim _status As Boolean
Dim _returnMessage As String
#End Region
#Region " Properties "
Public Property FileName() As String
Get
Return _fileName
End Get
Set(ByVal value As String)
_fileName = value
End Set
End Property
Public Property DirectoryName() As String
Get
Return _directoryName
End Get
Set(ByVal value As String)
_directoryName = value
End Set
End Property
Private ReadOnly Property ConversionMultiplier()
Get
_conversionValue = 1024
Return _conversionValue
End Get
End Property
Public Property ConversionType() As String
Get
Return _conversionType
End Get
Set(ByVal value As String)
_conversionType = value
End Set
End Property
Public ReadOnly Property FileSize() As Double
Get
Return _fileSize
End Get
End Property
Public Property TextToWrite() As String
Get
Return _textToWrite
End Get
Set(ByVal value As String)
_textToWrite = value
End Set
End Property
Public ReadOnly Property Status() As Boolean
Get
Return _status
End Get
End Property
Public ReadOnly Property ReturnMessage()
Get
Return _returnMessage
End Get
End Property
#End Region
The first set of actions we will be looking at is reading and writing to a text file. The first of these is the simplest, simply writing some text to a file. Here we will create a
TextWriter to create the file, then open it with a
StreamWriter, then we will use the
WriteLine Method to write the text to the file:
CODE
#Region " WriteToFile "
''' <summary>
''' Method for writing to a file
''' </summary>
Public Sub WriteToFile()
'always use a try...catch to deal
'with any exceptions that may occur
Try
'create a TextWriter then open the file
Using writer As TextWriter = New StreamWriter(_fileName)
'now write the message to the file
writer.WriteLine(_textToWrite)
End Using
Catch ex As Exception
'deal with any errors
_returnMessage = ex.Message
End Try
End Sub
#End Region
Here we create an instance of the
StreamReader class to open the file. We then use the
Write Method to write our text to the file. The variable
_textToWrite is the Property set in the calling method or application. In the above method we are introduced to 2 of the
TextWriter Members:
The main difference between the two is
WriteLine adds a line terminator to the end of the line, whereas Bb]Write[/b] you have to explicitly write one. Next we will look at writing to a specified line in an existing file, say like 5, but to do this you first much make sure the file you're writing to has at least 5 lines, otherwise you get an
ArgumentOutOfRangeException, as you'll be trying to write to a line that doesnt exist:
CODE
#Region " WriteToSpecifiedLine "
''' <summary>
''' Method for writing text to a specified line
''' </summary>
''' <param name="line">Line to insert text</param>
Public Sub WriteToSpecifiedLine(ByVal line As Integer)
'always use a try...catch to deal
'with any exceptions that may occur
Try
'make sure the file actually exists
'if not create and open it
If Not System.IO.File.Exists(_fileName) Then
System.IO.File.Create(_fileName)
End If
'open the file
Using reader As New StreamReader(_fileName)
'get the contents of the file
Dim contents As String = reader.ReadToEnd()
'insert the specified line
contents.Insert(line, _textToWrite)
'now we need to rewrite the text in the file
Using writer As New StreamWriter(_fileName, False)
'write the file with the new line
writer.Write(contents)
End Using
End Using
Catch ex As Exception
'deal with any errors
_returnMessage = ex.Message
End Try
End Sub
#End Region
For reading from a text file, lets start with something simple, say reading the first line we come to. Here we will open the file the same way we did when we first wrote to the file, except here we use
ReadLine to read a single line from the file, generally the first line in the file, then return that to the calling method:
CODE
#Region " ReadSingleLine "
''' <summary>
''' Method for reading a single line in a file
''' </summary>
''' <returns>The text in that line</returns>
Public Function ReadSingleLine() As String
'create a variable to hold our line
Dim lineText As String = String.Empty
'always use a try...catch to deal
'with any exceptions that may occur
Try
'create a new TextReader then open the file
Using reader As TextReader = New StreamReader(_fileName)
'read a single line from the file
lineText = reader.ReadLine()
End Using
Catch ex As FileNotFoundException
lineText = String.Empty
_returnMessage = ex.Message
Catch ex As Exception
'deal with any errors
_returnMessage = ex.Message
lineText = String.Empty
End Try
Return lineText
End Function
#End Region
Next we will take a look at reading from a text file. We will examine 2 different ways to accomplish this, we will loop through the file, reading a line at a time and adding it to our string variable, then we will read the whole file at once, adding it to our variable. In these examples we are introduced to the
TextReader Class. First, the read a line at a time example:
CODE
#Region " ReadFileByLine "
''' <summary>
''' Method for reading a text _fileName a line at a time
''' and adding it to a string to return to calling method
''' </summary>
''' <returns>_fileName contents in a string</returns>
Public Function ReadFileByLine() As String
'create a string variable to hold the file contents
Dim contents As String = String.Empty
'always use a try...catch to deal
'with any exceptions that may occur
Try
'create a new TextReader then open the file
Using reader As TextReader = New StreamReader(_fileName)
'loop through the entire file
While reader.Peek() <> -1
'add each line to the contents variable
contents += reader.ReadLine().ToString()
End While
End Using
Catch ex As FileNotFoundException
contents = String.Empty
_returnMessage = ex.Message
Catch ex As Exception
'deal with any errors
_returnMessage = ex.Message
contents = String.Empty
End Try
'return the results
Return contents
End Function
#End Region
NOTE: You will notice that I put all my code into #Region ... #End Region blocks, this allows me to easily separate any class or file into seperate regions, also making it easier to find certain code faster if I know which region it is in.
In this example we created a string variable
contents to hold the contents of the file, we then loop through the file a line at a time adding each line to the
contents variable. Here we are introduced to 2 of the
TextReader Members:
Peek is used to determine when we are at the end of the file, as long as it isn't returning -1 then there is more data in the file. Inside the loop we use
ReadLine to read each line individually, then appending it to the
variable.
There are 3 more Members of the
TextReader Class other than the
ReadLine mentioned above. They are:
Next to
ReadLine,
ReadToEnd is the most commonly used method of the
TextReader Class. As the name implies, it reads the entire file at once, no need to use
Peek or a loop. Lets take a look at an example employing this member:
CODE
#Region " ReadEntireFile "
''' <summary>
''' Method for reading an entire _fileName at once
''' </summary>
''' <returns>_fileName contents in a string</returns>
Public Function ReadEntireFile() As String
'create a string variable to hold the contents of the file
Dim contents As String = String.Empty
'always use a try...catch to deal
'with any exceptions that may occur
Try
'create a new TextReader and open our file
Using reader As TextReader = New StreamReader(_fileName)
'now read the entire file at once into our variable
contents = reader.ReadToEnd().ToString()
End Using
_status = True
Catch ex As FileNotFoundException
_status = False
contents = String.Empty
_returnMessage = ex.Message
Catch ex As Exception
_status = False
'deal with any errors
_returnMessage = ex.Message
contents = String.Empty
End Try
Return contents
End Function
#End Region
As in the previous example, we create our string variable
contents to hold the contents of the file, we create our
TextReader object using the
Using Statement, but unlike before, we don't use a loop. In this example we use
ReadToEnd to read the entire file on one pass. Both approaches, as with any programming approach, have their pro's and con's.
NOTE: Always put your logic inside a
Try...Catch block to catch and deal with any Exceptions that may have been raised during the process. Try to not always use
Catch ex As Exception as your only catch. Since you're writing the code you should have a small idea on what exceptions can occur in your code. Using the generic
Exception in the final Catch is fine.
In the next example, lets look at reading from a specified line in a text file. In this example, we split the lines in the file into an array, this allows us to know beforehand if this particular file has the number of lines the user is looking for, thus preventing receiving an
ArgumentOutOfRangeException. Then the user can pass an integer value representing which line they'd like to read.
CODE
#Region " ReadSpecifiedLine "
''' <summary>
''' Method to read a specified line in a text file
''' </summary>
''' <param name="line">Line number to read</param>
''' <returns></returns>
Public Function ReadSpecifiedLine(ByVal line As Integer) As String
'create a variable to hold the contents of the file
Dim contents As String = String.Empty
'create a variable to hold our line contents
Dim lineText As String = String.Empty
' always use a try...catch to deal
' with any exceptions that may occur
Try
'thanks for the idea from RodgerB at </dream.in.code>
Using lineByLine As New IO.StreamReader(_fileName)
Dim lineCount As Integer = 0
While Not lineByLine.EndOfStream
lineByLine.ReadLine()
If lineCount = line Then
lineText = lineByLine.ReadLine()
End If
lineCount += 1
End While
End Using
Catch ex As FileNotFoundException
lineText = String.Empty
_returnMessage = ex.Message
Catch ex As Exception
' deal with any errors
_returnMessage = ex.Message
End Try
Return lineText
End Function
#End Region
As with the previous examples we create the string variable [b]contents[/b], our [b]StreamReader[/b] Object, the difference here is we, as stated above, convert the lines in the file into a string array, then use the value passed by the user to read that specified line (using the value as the index of the array) and set the value of our variable to that line's value.
Lets take a look at once final Member of the [b]System.IO.File[/b] Class, the [url=http://msdn2.microsoft.com/en-us/library/system.io.file.exists.aspx]Exists Member[/url]. This member allows us to check if the specified file exists prior to opening and manipulating it.
[code]
#Region " ReadEntireFileIfExists "
''' <summary>
''' Method for reading a file if it exists
''' </summary>
''' <returns>_fileName contents in a string</returns>
Public Function ReadEntireFileIfExists() As String
'create a string variable to hold the contents of the file
Dim contents As String = String.Empty
'always use a try...catch to deal
'with any exceptions that may occur
Try
'check to see if the file exists
If System.IO.File.Exists(_fileName) Then
'create a new TextReader and open our file
Using reader As TextReader = New StreamReader(_fileName)
'now read the entire file at once into our variable
contents = reader.ReadToEnd()
End Using
_status = True
Else
_status = False
Throw New FileNotFoundException(_fileName + " could not be found")
End If
Catch ex As FileNotFoundException
'handle your errors here
_status = False
_returnMessage = ex.Message
contents = String.Empty
Catch ex As Exception
'handle your errors here
_status = False
_returnMessage = ex.Message
contents = String.Empty
End Try
Return contents
End Function
#End Region
Notice we use
Exists to determine if the file actually exists prior to using it. If the file exists we go ahead and read the file, if the file doesn't exist we throw a [b/FileNotFoundException[/b] letting the user know the file doesn't exist.
The last item we will look at in Part I of this tutorial is employing the
Structure I showed at the beginning of this tutorial to retrieve some attributes of a file, take those attributes and populate a Generic List(Of FileInformation) back to the calling method. Before I can show that method, however, I need to show the method that is referenced in this function where it checks to see if the file is open before we attempt to retrieve attributes from it. First, the method to check as see if the file is currently open:
CODE
#Region " IsFileOpen "
''' <summary>
''' Method to determine if a file is open
''' </summary>
''' <returns>Boolean value</returns>
Public Function IsFileOpen() As Boolean
'always use a try...catch to deal
'with any exceptions that may occur
Try
'check if the file exists, if it
'doesnt exist raise an error
If Not System.IO.File.Exists(_fileName) Then
_status = False
Throw New FileNotFoundException(_fileName + " could not be found!")
Else
Dim stream As FileStream = System.IO.File.OpenRead(_fileName)
stream.Close()
_status = True
End If
Catch
_status = True
End Try
Return _status
End Function
#End Region
Now for the implemntation of the
Structure:
CODE
#Region " GetFileInformation"
''' <summary>
''' Function to retrieve all the attributes of
''' the file provided
''' </summary>
''' <returns>A Generic list of the attributes</returns>
''' <remarks></remarks>
Public Function GetFileInformation() As List(Of FileInformation)
Dim finfo As FileInformation
Dim info As New FileInfo(_fileName)
Dim infoList As New List(Of FileInformation)
Try
'First make sure the file actually exists
If Not File.Exists(_fileName) Then
'File doesnt exist so set status to false
'and throw a FileNotFoundException
_status = False
Throw New FileNotFoundException(_fileName + " could not be found!")
Else
'File exists, now make sure it isnt already open
If Not IsFileOpen() Then
'File isnt open so we now set all the attributes of the file
finfo.FileCreatedOn = info.CreationTime.ToShortDateString
finfo.FileExtension = info.Extension
finfo.HiddenStatus = FileAttributes.Hidden
finfo.LastAccess = info.LastAccessTime.ToShortDateString
finfo.LastWrite = info.LastWriteTime.ToShortDateString
finfo.Name = info.Name
finfo.ReadOnlyStatus = FileAttributes.ReadOnly
finfo.FileLocation = info.DirectoryName
finfo.FileSize = info.Length
infoList.Add(finfo)
Else
'Throw an Exception letting the user know the file they're
'search is currently open and they need to close it first
Throw New Exception("The file you're searching is currently open. Please close the file and try again.")
End If
End If
Catch ex As FileNotFoundException
_status = False
_returnMessage = ex.Message
Return Nothing
Catch ex As Exception
_status = False
_returnMessage = ex.Message
Return Nothing
End Try
Return infoList
End Function
#End Region
The debate rages on about the use of Structure (struct in C#) but I am one on the ones for their use. Take this example, I needed an object to hold a small amount of related data, and instead of needing its own class I can encapsulate it into a Structure.
That is Part I of the System.IO in C# Tutorial, at the end of Part II I will be including the project files for this class.. I hope you have found this tutorial helpful and useful. Thanks for reading
This post has been edited by PsychoCoder: 28 Dec, 2007 - 06:29 AM