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.
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.
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.
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.
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.