Windows Server Application Server SQL Server Windows Windows Phone Sharepoint Programming

 Programming

# Programming Excel with VBA and .NET : Tasks in Visual Basic - Read and Write Files

8/12/2011 9:09:01 AM
There are a number of different ways to read and write files in Visual Basic, and which you choose depends on what you are trying to do, as described in Table 1.
##### Table 1. File-access techniques in Excel Visual Basic
TechniqueUse to
Intrinsic functionsRead or write simple datafiles
FileSystemObjectCreate files, folders, and control file attributes
Workbooks, Workbook objectsCreate, open, and save Excel workbook files; import datafiles into workbooks
XMLMap objectImport or export XML datafiles from a workbook

In short, you shouldn't assume the Visual Basic intrinsic functions are the best way to read and write files in all situations. Actually, I prefer the FileSystemObject for most general file-access tasks, but it's important to be thorough, so I'll cover the intrinsic file-access functions here (Table 2).

##### Table 2. Visual Basic's intrinsic file-access functions
CategoryFunctionUse to
AccessCloseClose an open file
FileCopyCopy a file
FreeFileGet a file number for Open
Lock...UnlockPrevent others from accessing all or part of a file
LOFGet the length of an open file in bytes
OpenOpen a file
ResetClose all open files
AttributesFileAttrGet the attributes of an open file
FileDateTimeGet the date that a file was created or changed
FileLenGet the length of a file in bytes before opening it
GetAttrGet the attributes of a file, folder, or volume label
SetAttrChange the attributes of a file, folder, or volume label
DrivesChDirSet the current folder
ChDriveSet the current drive
CurDirGet the current folder
MkDirCreate a new folder
RmDirDelete an empty folder
ManageDirList files in a folder
KillDelete a file
NameRename a file
EOFTest you have reached the end of the file
Input #Read records from an open sequential file
Line Input # Read a line from an open sequential file
LocReturn the current position within a file
SeekGet or set the current position within a file
WritePrint #Write a line to an open sequential file
PutWrite data to an open binary or random-access file
SpcInsert blank spaces in a sequential file
TabInsert tab characters in a sequential file
Width #Set the width of a sequential file
Write #Write records to a sequential file

The functions in Table 2 reflect the fact that there are three different types of file access in Visual Basic:

Sequential access

Reads files one line at a time

Random access

Reads files as a collection of fixed-length records

Binary access

Reads files as an arbitrary number of bytes

All of these types of access follow the same pattern, which is based on a very old programming concept called file handles :

1. Use FreeFile to get a number that is available for use as a file handle.

2. Open the file using that number and the chosen file-access method.

3. Read data from the file using Input # (sequential access) or Get (random or binary access), or write data using Print #, Write # (sequential), or Put (random or binary).

4. Close the file handle.

 The modern approach, such as that used by the FileSystemObject, is to use object references rather than numeric file handles.

Of the three types of file access, binary is the most useful in today's world because it allows you to read an entire file into a variable with a single statement. It is by far the fastest way to get the contents of a file. The following QuickRead function opens and reads a file and returns the data it contains as a string variable:

    ' Reads a file into a string.    Function QuickRead(fname As String) As String        Dim i As Integer, res As String, l As Long        ' Get a free file handle.        i = FreeFile        ' Get the length of the file        l = FileLen(fname)        ' Create a string to contain the data.        res = Space(l)        ' Open the file.        Open fname For Binary Access Read As #i        ' Read the whole file into res.        Get #i, , res        ' Close the file        Close i        ' Return the string.        QuickRead = res    End Function

How big of a file can you read this way? Pretty big! I had no problem loading an 8.4 MB art file using this technique. String variables can be very large in Visual Basic. Similarly, you can write files very quickly with binary access. The following QuickWrite function saves a string as a file and returns True if it succeeded:

    ' Writes data to a file.    Function QuickWrite(data As String, fname As String, _      Optional overwrite As Boolean = False) As Boolean        Dim i As Integer, l As Long        ' If file exists and overwrite is True, then        If Dir(fname) <> "" Then            If overwrite Then                ' delete the file.                Kill fname            Else                ' else, return False and exit.                QuickWrite = False                Exit Function            End If        End If        ' Get a free file handle.        i = FreeFile        ' Get the length of the file        l = Len(data)        ' Open the file.        Open fname For Binary Access Write As #i Len = l        ' Write the string to the file.        Put #i, , data        ' Close the file        Close i        ' Return True.        QuickWrite = True    End Function

This approach was first pointed out to me by Mark Chase, senior developer on Basic at Microsoft. He deserves credit for clear thinking and also for being a darn nice guy. You can test that these functions work by running the following code from the sample workbook:

    Sub DemoQuickReadWrite( )        Dim pth As String, data As String        ' Get the folder that this workbook is in.        pth = ThisWorkbook.Path        ' Read the ReadMe.txt file.        data = QuickRead(pth & "\in.txt")        ' Display the file        Debug.Print data        ' Change the file.        data = Replace(data, "text", "data")        ' Save the file.        Debug.Print QuickWrite(data, pth & "\out.txt", True)    End Sub

#### 1. Sequential Access

Sequential access reads and writes files one line at a time. In the past, sequential access was often used to write reports or other data to human-readable files. For example, the following WriteArray function writes a two-dimensional array to disk as a comma-delimited file using sequential access:

    ' Writes a two-dimensional array to a comma-delimited file.    ' (Use to create CSV file out of a selected range.)    Function WriteArray(arr As Variant, fname As String, _      Optional overwrite As Boolean = False) As Boolean        Dim lb1 As Long, lb2 As Long, ub1 As Long, ub2 As Long        Dim i As Integer, rows As Long, cols As Long, rec As String        ' If arr isn't an array, return False and exit.        If Not IsArray(arr) Then WriteArray = False: Exit Function        ' Get bounds for For loops.        lb1 = LBound(arr, 1)        lb2 = LBound(arr, 2)        ub1 = UBound(arr, 1)        ub2 = UBound(arr, 2)        ' If file exists and overwrite is True, then        If Dir(fname) <> "" Then            If overwrite Then                ' delete the file.                Kill fname            Else                ' else, return False and exit.                WriteArray = False                Exit Function            End If        End If        ' Get a free file handle.        i = FreeFile        ' Open the file.        Open fname For Append As #i        ' For each row in the array.        For rows = lb1 To ub1            ' For each column in the array.            For cols = lb2 To ub2                rec = rec & arr(rows, cols) & ", "            Next            ' Remove the last ", " from rec.            rec = Left(rec, Len(rec) - 2)            ' Write rec to the file.            Print #i, rec            ' Clear rec            rec = ""        Next        ' Close the file        Close i        ' Return True.        WriteArray = True    End Function
That looks complicated, but the actual file-access code (in bold) is really very simple and follows the pattern described previously: get a file handle, open the file, read or write to the file, close the file. Sequential access is suited to this task since you are building the string data one line at a time as you loop over the rows in the array.

Perhaps a better approach to this task would be to build a string from the array in one procedure and then save that string using the QuickWrite function. That approach would isolate file access in one place (QuickWrite) instead of integrating it into the task of converting the array into a string. The following code shows that alternate approach:

    ' Better approach -- don't integrate file access within    ' array conversion task.    Function TableToCSV(arr As Variant) As String        Dim lb1 As Long, lb2 As Long, ub1 As Long, ub2 As Long        Dim rows As Long, cols As Long, rec As String        ' If arr is not an array, return "" and exit.        If Not IsArray(arr) Then TableToCSV = "": Exit Function        ' Get bounds for For loops.        lb1 = LBound(arr, 1)        lb2 = LBound(arr, 2)        ub1 = UBound(arr, 1)        ub2 = UBound(arr, 2)        For rows = lb1 To ub1            For cols = lb2 To ub2                rec = rec & arr(rows, cols) & ", "            Next            ' Remove last ", " and add carriage return/line feed.            rec = Left(rec, Len(rec) - 2) & vbCrLf        Next        TableToCSV = rec    End Function

Using TableToCSV instead of WriteArray involves the extra step of calling QuickWrite, but the logic is still very clear:

    Sub DemoTableToCSV( )        Dim arr As Variant, data As String, pth As String        pth = ThisWorkbook.Path        ' Get cells from the active worksheet.        arr = ActiveSheet.UsedRange.Value        ' If the range contains cells.        If IsArray(arr) Then            ' Convert array to CSV.            data = TableToCSV(arr)            If data <> "" Then                ' Save the result                QuickWrite data, pth & "\selection.csv", True                ' Display the result                Debug.Print data            End If        End If    End Sub

#### 2. Random Access

Random-access files are read or written one record at a time. In this case, record usually means a fixed-size data structure identified by a user-defined type. Because Visual Basic knows the length of each record, you can jump to any record in the file using the Seek statement (that's what makes the access random).

In order to use random access , you must first define the structure of your record with a Type statement. You then declare a variable with that type and use it to read and/or write records to the file. I'm not going to show you how to do all that, because XML files and databases both provide a much better approach for storing and retrieving structured data.

Why is random access not such a great approach? A few reasons:

• The records are fixed-length by definition, which means names, addresses, and other variable-length data must be stored in fixed-length strings. You have to correctly guess the maximum size of those items during design.

• Changes to your data structure, such as adding a field, means you have to convert all of your existing datafiles. You have to write code to open, convert, and save files using the new structure. (In programming circles this is called tying your data structure to your implementation, and it's a bad thing.)

• You're programming in Excel! You already have better tools for doing these types of tasks.

In addition to reading and writing files, you also often need to manage the files on a computer. The most common tasks are listed in Table 3.

##### Table 3. Common tasks for Visual Basic's intrinsic file functions
Check if file existsDirAlso used to list files in a folder.
Delete a fileKillDeletes a file if it is not locked or read-only.
Get the current folderCurDirExcel may change the current folder when a workbook is saved or opened by the user.
Change current folderChDirYou can use characters like .. to move up one folder.
Change current driveChDriveOnly the first letter from the argument is used.
Create a folderMkDirMay include path specifiers like . (current folder) or .. (up one folder). Does not change the current folder.
Delete a folderRmDirFolder must be empty before it can be deleted.
Get/change file attributesFileAttrFile attributes include hidden, read-only, archive.
Make a backup copyFileCopyCopies an existing file to a new filename.
Rename a fileNameChanges a filename.

In general, it is not a good idea to get the current folder (CurDir) or change the current folder (ChDir) from Visual Basic when working with Excel because saving or opening a file from the Excel user interface may subsequently change the current folder. It is a better practice to use the path properties provided by Excel objects when working with folders in Excel.

For example, the following code displays the paths available for various Excel objects:

    Sub ShowPaths( )        Dim wbPth As String, appPth As String, stPth As String, _          altPth As String, tpPth As String, adPth As String        wbPth = ThisWorkbook.Path        appPth = Application.Path        stPth = Application.StartupPath        altPth = Application.AltStartupPath        tpPth = Application.TemplatesPath        adPth = Application.AddIns(1).Path        Debug.Print "Workbook path:", wbPth        Debug.Print "Application path:", appPth        Debug.Print "Startup path: ", stPth        Debug.Print "Alt startup path:", altPth        Debug.Print "Template path:", tpPth        Debug.Print "Add-in path:   ", adPth    End Sub

I often use ThisWorkbook.Path within my samples to get or save files associated with the current workbook. That strategy keeps all of the related files in the same folder, so it is easier to copy the samples to a new location or to install them on your computer. Alternately, you may choose to create a fixed folder location for use in your code such as shown here:

    ' A fixed path.    Const SAMPLEPTH = "\Excel\Samples"    ' Run once to create folder.    Sub CreateSamplesFolder( )        ' Create the SAMPLEPTH folder        On Error Resume Next        MkDir "\Excel"        MkDir "\Excel\Samples"        If Err Then _          MsgBox ("Couldn't create folder. It may already exist.")    End Sub

Using a fixed location for your files poses the problem illustrated by the preceding exception handling: the folder may already exist! That's another reason to use the ThisWorkbook.Path approach.

You can use the Dir function to check whether a file exists in a folder or to get a list of all of the files in a folder. When getting a list of files, Dir acts a little strangely. The first time you call it, specify the folder you want to search; then call Dir without an argument to get the next file in the folder, as shown here:

    Function GetFiles(filepath As String) As Variant        Dim arr( ) As String, fname As String, count As Integer        ' Get the first file.        fname = Dir(filepath & "\*")        Do Until fname = ""            count = count + 1            ReDim Preserve arr(count)            arr(count - 1) = fname            ' Get next file.            fname = Dir( )        Loop        ' Return the array        GetFiles = arr    End Function

Dir does not order the files it returns alphabetically, so you may need to sort the list before displaying it. For example, the following code uses the GetFiles function to list the files in the current workbook's folder:

     Sub DemoGetFiles( )        Dim flist As Variant, str As String        flist = GetFiles(ThisWorkbook.Path)        ' Sort the file list        Text.SortArray (flist)        str = Join(flist, vbCrLf)        Debug.Print str    End Sub

The FileSystemObject provides more extensive methods for working with files, folders, and drives.

 Top 10

- First look: Apple Watch

- 3 Tips for Maintaining Your Cell Phone Battery (part 1)

- 3 Tips for Maintaining Your Cell Phone Battery (part 2)
 Video Channel Windows Office Database Application Server