Processing Text Files for Output (Writing)

 

In the previous section, we saw how to use the FileStream and StreamReader classes of the System.IO namespace to read a text file in both fixed-length and pipe-delimited formats. We also saw how to use the Input function of the Microsoft.VisualBasic namespace to easily read a comma-delimited file that also contained fields enclosed in quotes.

 

In this section we will see how to use the FileStream and StreamWriter classes of the System.IO namespace to write a text file in both fixed-length and pipe-delimited formats. We will also see how to use the WriteLine function of the Microsoft.VisualBasic namespace to easily create a comma-delimited file that contains text fields enclosed in quotes; we will also see how to use the PrintLine function of the MS.VB namespace to create a fixed-length file, noting the similarities and differences between that and the StreamWriter technique.

 

As in the previous section, the file that will be used for all examples in this section is a simplified employee file, which consists of the following fields:

 

            Field                                         Data Type*

            Employee Name                        String

            Department Number                  Integer

            Job Title                                   String

            Hire Date                                  Date

            Hourly Rate                               Single

 

*Please note that the data types for these fields are the data types of the variables into which these fields will be stored.  In the text file, all fields will be represented as a string of characters.

 

Example 1: Writing a Text File with Fixed-Length Fields

 

For the fixed-length file, we want to write the records in the following format:

 

Columns                                   Field / Notes

            1-20                                          Employee Name (last name first, 20 characters, space-filled on the right)

            21-24                                        Department Number (up to 4 digits, space-filled on the left)

            25-29                                        (unused – should contain blank spaces)

            30-50                                        Job Title (21 characters, space-filled on the right)

            51-60                                        Hire Date (month/day/year format,  10 characters, space-filled on the right)

            61-65                                        Hourly Rate (a value between 0 and 99.99, occupies 5 positions, space-filled on the left)

 

In this example program, the user will be prompted to enter each field on the console, one-by-one (employee name, then department, job title, hire date, and hourly rate). As the user enters each field, the program will check to make sure the data is valid (and if invalid, will show a message and re-prompt for that same field). When all five fields for a record have been entered, the program will write a fixed-length record to the output file and start all over again with the prompts for the next record, beginning with the employee name. If the user presses the Enter key without entering a value for the employee name, this signals the program that the user is done, and the program will end.

 

A screen-shot of a sample run, where the user has entered two records, is shown below:

 

 

Looking at the output file in a text editor such as Notepad, we see:

 

FARLEY,FRANK         123     DBA                  1/1/2001  40.00

GARFIELD,GARY         43     IT MANAGER           3/5/1992  60.00

 

 

The code for the program is shown below. Note that Sub Main is fairly streamlined, due to the fact that the prompts and data validation for each field are contained in their own functions (GetEmployee, GetDepartment, GetJobTitle, GetHireDate, and GetHourlyRate) which are invoked by Sub Main when needed.

 

Imports System.IO

 

Module Module1

 

    Sub Main()

 

        Dim strFileName As String = My.Application.Info.DirectoryPath _

                          & "\empout_fixed.txt"

        Dim objFS As New FileStream(strFileName, FileMode.Create, FileAccess.Write)

        Dim objSW As New StreamWriter(objFS)

 

        Dim strEmpName As String

        Dim intDeptNbr As Integer

        Dim strJobTitle As String

        Dim dtmHireDate As Date

        Dim sngHrlyRate As Single

 

        ' Set up an "input loop" to prompt the user for data ...

 

        ' "Priming" input:

        strEmpName = GetEmployeeName()

 

        ' The loop will continue until the user enters a zero-length string for the

        ' employee name ...

        Do Until strEmpName = ""

 

            ' Prompt the user for each field ...

            intDeptNbr = GetDepartment()

            strJobTitle = GetJobTitle()

            dtmHireDate = GetHireDate()

            sngHrlyRate = GetHourlyRate()

 

            ' Write out the record to the file ...

            objSW.WriteLine(strEmpName.PadRight(20) & _

                            intDeptNbr.ToString.PadLeft(4) & _

                            Space(5) & _

                            strJobTitle.PadRight(21) & _

                            Format(dtmHireDate, "M/d/yyyy").PadRight(10) & _

                            Format(sngHrlyRate, "Standard").PadLeft(5))

 

            Console.WriteLine("Record was written to the output file.")

            Console.WriteLine("")

 

            ' Start a new record by prompting for the employee name ...

            strEmpName = GetEmployeeName()

        Loop

 

        objSW.Close()

 

        Console.WriteLine("")

        Console.WriteLine("File creation complete. Press Enter to close this window.")

        Console.ReadLine()

 

    End Sub

 

    Private Function GetEmployeeName() As String

 

        Dim strTest As String

        Dim blnValidInput As Boolean

 

        Do

            Console.Write("Enter Employee Name (1-20 characters / nothing to quit): ")

            strTest = Console.ReadLine()

            strTest.Trim()

            blnValidInput = True

            If strTest.Length > 20 Then

                blnValidInput = False

                Console.WriteLine("Invalid employee name.")

            End If

        Loop Until blnValidInput

 

        Return (strTest)

 

    End Function

 

    Private Function GetDepartment() As Integer

 

        Dim strTest As String

        Dim blnValidInput As Boolean

        Dim intX As Integer

 

        Do

            Console.Write("Enter Dept (1 to 4 digits): ")

            strTest = Console.ReadLine()

            blnValidInput = True

            If strTest.Length < 1 Or strTest.Length > 4 Then

                blnValidInput = False

            Else

                For intX = 0 To (strTest.Length - 1)

                    If Not (Char.IsDigit(strTest.Chars(intX))) Then

                        blnValidInput = False

                        Exit For

                    End If

                Next

            End If

            If Not blnValidInput Then

                Console.WriteLine("Invalid department number.")

            End If

        Loop Until blnValidInput

 

        Return (CInt(strTest))

 

    End Function

 

    Private Function GetJobTitle() As String

 

        Dim strTest As String

        Dim blnValidInput As Boolean

 

        Do

            Console.Write("Enter Job Title (1 to 21 characters): ")

            strTest = Console.ReadLine()

            blnValidInput = True

            If strTest.Length < 1 Or strTest.Length > 21 Then

                Console.WriteLine("Invalid job title.")

                blnValidInput = False

            End If

        Loop Until blnValidInput

 

        Return (strTest)

 

    End Function

 

    Private Function GetHireDate() As Date

 

        Dim strTest As String

        Dim blnValidInput As Boolean

 

        Do

            Console.Write("Enter Hire Date (month/day/year): ")

            strTest = Console.ReadLine()

            blnValidInput = True

            If Not IsDate(strTest) Then

                Console.WriteLine("Invalid hire date.")

                blnValidInput = False

            End If

        Loop Until blnValidInput

 

        Return (CDate(strTest))

 

    End Function

 

    Private Function GetHourlyRate() As Single

 

        Dim strTest As String

        Dim blnValidInput As Boolean

 

        Do

            Console.Write("Enter Hourly Rate (0 to 99.99): ")

            strTest = Console.ReadLine()

            blnValidInput = True

            If Not IsNumeric(strTest) Then

                blnValidInput = False

            ElseIf Val(strTest) < 0 Or Val(strTest) > 99.99 Then

                blnValidInput = False

            End If

            If Not blnValidInput Then

                Console.WriteLine("Invalid hourly rate.")

            End If

        Loop Until blnValidInput

 

        Return (CSng(strTest))

 

    End Function

 

End Module

 

A synopsis of the code follows:

 

Note that first, your program must import the System.IO namespace. In the Main procedure, the variable to hold the full path and filename of the output file is declared and initialized (note that the location of the file is My.Application.Info.DirectoryPath which, as discussed in the previous section, refers to the directory in which your program is running, normally \bin\Debug in your application directory). The name of the file is "empout_fixed.txt". The FileStream object is the established to hold a reference to the file (note that it is declared in "Create" mode with "Write" access). Then, the StreamWriter object is declared.

 

The StreamWriter class is used to write a stream of characters. In the .NET framework, a stream, in general terms, is the flow of data from one location to another. In the case of these examples, a stream refers to the text file that will be processed by the sample programs. The syntax for declaring a StreamWriter object is:

 

       Dim variable As New StreamReader(stream)

 

where stream is an object representing the stream of characters to be written (in this case a text file).

 

Variables to hold the data fields are then declared. The processing starts with a "priming" input outside the main loop. This calls the GetEmployeeName function which will prompt the user for the employee name and return the string entered. The loop's termination condition tests whether the name entered is a zero-length string. As long as the user entered "something", the loop will continue. In the loop, prompts are made and input is validated and returned for the department, job title, hire date, and hourly rate fields by calling the GetDepartment, GetJobTitle, GetHireDate, and GetHourlyRate functions, respectively. The fixed-length record is written to the file by the WriteLine method of the StreamWriter object (which was defined with the variable name objSW). The string that is passed to the method is a concatenation of the variables holding the values for the data fields, padded and formatted appropriately. A message is displayed on the console telling the user that the record was written, then the GetEmployeeName function is called as the last statement of the loop, which prompts the user for the employee name, for another possible pass through the loop to write another record. When the user finally responds to the employee name prompt with just the Enter key, the loop ends, we close the StreamWriter object and let the user know the writing of the file is complete.

 

Download the VB project code for Example 1 here.

 

 

Example 2: Writing a Text File with Pipe-Delimited Fields

 

In the second example, a file with pipe-delimited fields is produced. Using the same data entered in Example 1, an inspection of the output file in a text editor such as Notepad would show:

 

FARLEY,FRANK|123|DBA|1/1/2001|40

GARFIELD,GARY|43|IT MANAGER|3/5/1992|60

 

The code for the Example 2 program is identical to that of Example 1, except that the output file is named "empout_pipe.txt":

 

        Dim strFileName As String = My.Application.Info.DirectoryPath _

                          & "\empout_pipe.txt"

 

and in the objSW.WriteLine statement, the string that is passed consists of the variables holding the values for the data fields, formatted as necessary, concatenated with the pipe character ("|"):

 

            objSW.WriteLine(strEmpName & "|" & _

                            intDeptNbr.ToString & "|" & _

                            strJobTitle & "|" & _

                            Format(dtmHireDate, "M/d/yyyy") & "|" & _

                            sngHrlyRate.ToString)

 

 

Download the VB project code for Example 2 here.

 

 

Example 3: Writing a Text File with Comma-Delimited Fields (String Fields Enclosed in Quotes)

 

In the third example, a file with comma-delimited fields is produced. String fields in this file will also be enclosed in quotes. Using the same data entered in Example 1, an inspection of the output file in a text editor such as Notepad would show:

 

"FARLEY,FRANK",123,"DBA","1/1/2001",40

"GARFIELD,GARY",43,"IT MANAGER","3/5/1992",60

 

This example uses the Microsoft.VisualBasic namespace functions for all I/O, so the System.IO namespace need not be included. The function that produces the output record is WriteLine (not to be confused with the WriteLine method of the System.IO StreamWriter class – in the MS.VB namespace, the WriteLine function is a retooled version of the Write statement from pre-.NET versions of Visual Basic).

 

The syntax of the MS.VB namespace WriteLine function, as it is used in this example is:

 

                   WriteLine (filenumber[, expression1[, expression2 ...[, expression n]]])

 

where filenumber is the file number referenced used to open the file with the FileOpen function, and expression1 through expression n is the list of expressions (variables, constants, etc.) that are to be written to the output file. When executed, the items in the expression list are automatically written to the file with commas separating the expressions for each other; and string items are automatically enclosed in quotes.

 

It should be noted that files written with the WriteLine function are most commonly read back with the Input function. Also note that while we are treating the Hire Date field as a string, the WriteLine function can write a "date" expression to a text file, in which case the date would be written to the file in yyyy-mm-dd format and would be enclosed in pound signs (#) rather than quotes; and if the file is read back with the Input function, the Input function would recognize a field enclosed with pound signs as a date data type.

 

The code for the Sub Main portion of Example 3 is shown below:

 

    Sub Main()

 

        Dim strFileName As String = My.Application.Info.DirectoryPath _

                          & "\empout_comma.txt"

        Dim intFileNbr As Integer = FreeFile()

 

        Dim strEmpName As String

        Dim intDeptNbr As Integer

        Dim strJobTitle As String

        Dim dtmHireDate As Date

        Dim sngHrlyRate As Single

 

        FileOpen(intFileNbr, strFileName, OpenMode.Output, OpenAccess.Write)

 

        ' Set up an "input loop" to prompt the user for data ...

 

        ' "Priming" input:

        strEmpName = GetEmployeeName()

 

        ' The loop will continue until the user enters a zero-length string for the

        ' employee name ...

        Do Until strEmpName = ""

 

            ' Prompt the user for each field ...

            intDeptNbr = GetDepartment()

            strJobTitle = GetJobTitle()

            dtmHireDate = GetHireDate()

            sngHrlyRate = GetHourlyRate()

 

            ' Write out the record to the file ...

            WriteLine(intFileNbr, _

                      strEmpName, _

                      intDeptNbr, _

                      strJobTitle, _

                      Format(dtmHireDate, "M/d/yyyy"), _

                      sngHrlyRate)

 

            Console.WriteLine("Record was written to the output file.")

            Console.WriteLine("")

 

            ' Start a new record by prompting for the employee name ...

            strEmpName = GetEmployeeName()

        Loop

 

        FileClose(intFileNbr)

 

        Console.WriteLine("")

        Console.WriteLine("File creation complete. Press Enter to close this window.")

        Console.ReadLine()

 

    End Sub

 

The GetEmployee, GetDepartment, GetJobTitle, GetHireDate, and GetHourlyRate functions referenced in code above are exactly the same as in the previous examples.

 

A synopsis of the code follows:

 

The flow of this program is identical to that of the previous examples, but we are using all MS.VB namespace functions instead of the System.IO FileStream and StreamWriter classes. In the Main procedure, the variable to hold the full path and filename of the output file is declared and initialized (in this example, the filename is "empout_comma.txt"). An Integer  variable to hold the filenumber is declared and initialized with the FreeFile function. Variables to hold the data fields are then declared. The file is then opened with the FileOpen function using output mode with write access.

 

As in the previous examples, the processing starts with a "priming" input outside the main loop. This calls the GetEmployeeName function which will prompt the user for the employee name and return the string entered. The loop's termination condition tests whether the name entered is a zero-length string. As long as the user entered "something", the loop will continue. In the loop, prompts are made and input is validated and returned for the department, job title, hire date, and hourly rate fields by calling the GetDepartment, GetJobTitle, GetHireDate, and GetHourlyRate functions, respectively. The comma-delimited record is then written to the file with the WriteLine function, passing it the filenumber followed by the list of variables holding the data fields. A message is displayed on the console telling the user that the record was written, then the GetEmployeeName function is called as the last statement of the loop, which prompts the user for the employee name, for another possible pass through the loop to write another record. When the user finally responds to the employee name prompt with just the Enter key, the loop ends, we close the file using the FileClose function, and let the user know the writing of the file is complete.

 

Download the VB project code for Example 3 here.

 

 

Example 4: Writing a Text File with Fixed-Length Fields (Using the PrintLine Function)

 

This example uses the MS.VB namespace PrintLine function to produce a fixed-length file. The output is identical to that produced in the first example:

 

FARLEY,FRANK         123     DBA                  1/1/2001  40.00

GARFIELD,GARY         43     IT MANAGER           3/5/1992  60.00

 

The syntax of the MS.VB namespace PrintLine function, as it is used in this example is:

 

                   PrintLine (filenumber[, expression1[, expression2 ...[, expression n]]])

 

where filenumber is the file number referenced used to open the file with the FileOpen function, and expression1 through expression n is the list of expressions (variables, constants, etc.) that are to be written to the output file. However, unlike the WriteLine function, no automatic formatting of the data occurs; each item in the expression list is written adjacent to each other with no added separation characters or quotes. In that PrintLine is a retooled version of the Print statement found in pre-.NET versions of VB, there are two built-in functions that can be used within the PrintLine expression list: TAB and SPC.

 

SPC(n) simply inserts n number of blank spaces into the output line being built. So the statement PrintLine (1, "ABC", SPC(5), "DEF")would result in a line written to the output file containing "ABC", followed by five blank spaces, followed by "DEF".

 

TAB(n) is actually a directive that tells PrintLine to position the next output item at position n in the output line. So the statement PrintLine (1, "ABC", TAB(20), "DEF")would result in a line written to the output file containing "ABC", followed by 16 blank spaces, followed by "DEF". This is because first, "ABC" would be written, occupying the first three positions – then TAB(20) says to skip to position 20 in the output line and write "DEF". So 16 positions needed to be bypassed (actually space-padded) to give the 19 positions prior to the 20th where "DEF" would start.

 

The code for the Example 4 program is identical to that of Example 3, except that the output file is named "empout_fixed_pl.txt":

 

        Dim strFileName As String = My.Application.Info.DirectoryPath _

                          & "\empout_fixed_pl.txt"

 

and the PrintLine function is used instead of the WriteLine function:

 

            PrintLine(intFileNbr, _

                      strEmpName, _

                      TAB(21), _

                      intDeptNbr.ToString.PadLeft(4), _

                      SPC(5), _

                      strJobTitle, _

                      TAB(51), _

                      Format(dtmHireDate, "M/d/yyyy"), _

                      TAB(61), _

                      Format(sngHrlyRate, "Standard").PadLeft(5))

 

Download the VB project code for Example 4 here.