7.6 Truncating a File
If you open an existing file (using fileio.open) for output and write data to that file, it overwrites the existing data from the start of the file. However, if the new data you write to the file is shorter than the data originally appearing in the file, the excess data from the original file, beyond the end of the new data you've written, will still appear at the end of the new data. Sometimes this might be desirable, but most of the time you'll want to delete the old data after writing the new data.
One way to delete the old data is to use the fileio.openNew function to open the file. The fileio.openNew function automatically deletes any existing file so only the data you write to the file will be present in the file. However, there may be times when you may want to read the old data first, rewind the file, and then overwrite the data. In this situation, you'll need a function that will truncate the old data at the end of the file after you've written the new data. The fileio.truncate function accomplishes this task. This function uses the following calling syntax:fileio.truncate( fileHandle );
Note that this function does not close the file. You still have to call fileio.close to commit the data to the disk.
The following sample program demonstrates the use of the fileio.truncate function:program TruncateDemo; #include( "stdlib.hhf" ) static fileHandle:dword; u:uns32; begin TruncateDemo; fileio.openNew( "myfile.txt" ); mov( eax, fileHandle ); for( mov( 0, ecx ); ecx < 20; inc( ecx )) do fileio.put( fileHandle, (type uns32 ecx), nl ); endfor; // Okay, let's rewind to the beginning of the file and // rewrite the first ten lines and then truncate the // file at that point. fileio.rewind( fileHandle ); for( mov( 0, ecx ); ecx < 10; inc( ecx )) do fileio.put( fileHandle, (type uns32 ecx), nl ); endfor; fileio.truncate( fileHandle ); // Rewind and display the file contents to ensure that // the file truncation has worked. fileio.rewind( fileHandle ); while( !fileio.eof( fileHandle )) do // Read and display the next item from the file: fileio.get( fileHandle, u ); stdout.put( "u=", u, nl ); fileio.readLn( fileHandle ); endwhile; fileio.close( fileHandle ); end TruncateDemo; Program 7.7 Using fileio.truncate to Eliminate Old Data From a File
7.7 File Utility Routines
The following subsections describe fileio functions that manipulate files or return meta-information about files (e.g., the file size and attributes).
7.7.1 Copying, Moving, and Renaming Files
Some very useful file utilities are copying, moving, and renaming files. For example, you might want to copy a file an application has created in order to make a backup copy. Moving files from one subdirectory to another, or even from one disk to another is another common operation. Likewise, the need to change the name of a file arises all the time. In this section we'll take a look at the HLA Standard Library routines that accomplish these operations.
Copying a file is a nearly trivial process under Windows1. All you've got to do is open a source file, open a destination file, then read the bytes from the source file and write them to the destination file until you hit end of file. Unfortunately, this simple approach to copying a file can suffer from performance problems. Windows provides an internal function to copy files using a high performance algorithm (Linux does not provide this call). The HLA Standard Library fileio.copy function provides an interface to this copy operation. The copy a file using the fileio.copy procedure, you'd use the following call sequence:fileio.copy( sourcefileName, destFileName, failIfExists );
The sourceFileName and destFileName parameters are strings that specify the pathnames of the source and destination files. These can be string constants or variables. The last parameter is a boolean variable that specifies what should happen if the destination file exists. If this parameter contains true and the file already exists, then the function will fail; if failIfExists is false, the fileio.copy routine will replace the existing destination file with a copy of the source file. In either case, of course, the source file must exist or this function will fail. This function returns a boolean success/failure result in the EAX register. It returns true if the function returns TRUE in EAX.
Program 7.8 demonstrates the use of this function to copy a file:program CopyDemo; #include( "stdlib.hhf" ) begin CopyDemo; // Make a copy of myfile.txt to itself to demonstrate // a true "failsIfExists" parameter. if( !fileio.copy( "myfile.txt", "myfile.txt", true )) then stdout.put( "Did not copy `myfile.txt' over itself" nl ); else stdout.put( "Whoa! The failsIfExists parameter didn't work." nl ); endif; // Okay, make a copy of the file to a different file, to verify // that this works properly: if( fileio.copy( "myfile.txt", "copyOfMyFile.txt", false )) then stdout.put( "Successfully copied the file" nl ); else stdout.put( "Failed to copy the file (maybe it doesn't exist?)" nl ); endif; end CopyDemo; Program 7.8 Demonstration of a fileio.copy Operation
To move a file from one location to another might seem like another trivial task - all you've got to do is copy the file to the destination and then delete the original file. However, these scheme is quite inefficient in most situations. Copying the file can be an expensive process if the file is large; Worse, the move operation may fail if you're moving the file to a new location on the same disk and there is insufficient space for a second copy of the file. A much better solution is to simply move the file's directory entry from one location to another on the disk. Win32's disk directory entries are quite small, so moving a file to a different location on the same disk by simply moving its directory entry is very fast and efficient. Unfortunately, if you move a file from one file system (disk) to another, you will have to first copy the file and then delete the original file. Once again, you don't have to bother with the complexities of this operation because Windows has a built-in function that automatically moves files for you. The HLA Standard Library's fileio.move procedure provides a direct interface to this function (available only under Windows). The calling sequence isfileio.move( source, dest );
The two parameters are strings providing the source and destination filenames. This function returns true or false in EAX to denote the success or failure of the operation.
Not only can the fileio.move procedure move a file around on the disk, it can also move subdirectories around. The only catch is that you cannot move a subdirectory from one volume (file system/disk) to another.
If both the destination and source filenames are simple filenames, not a pathnames, then the fileio.move function moves the source file from the current directory back to the current directory. Although this seems rather weird, this is a very common operation; this is how you rename a file. The HLA Standard Library does not have a separate "fileio.rename" function. Instead, you use the fileio.move function to rename files by moving them to the same directory but with a different filename. Program 7.9 demonstrates how to use fileio.move in this capacity.program FileMoveDemo; #include( "stdlib.hhf" ) begin FileMoveDemo; // Rename the "myfile.txt" file to the name "renamed.txt". if( !fileio.move( "myfile.txt", "renamed.txt" )) then stdout.put ( "Could not rename `myfile.txt' (maybe it doesn't exist?)" nl ); else stdout.put( "Successfully renamed the file" nl ); endif; end FileMoveDemo; Program 7.9 Using fileio.move to Rename a File
7.7.2 Computing the File Size
Another useful function to have is one that computes the size of an existing file on the disk. The fileio.size function provides this capability. The calling sequences for this function arefileio.size( filenameString ); fileio.size( fileHandle );
The first form above expects you to pass the filename as a string parameter. The second form expects a handle to a file you've opened with fileio.open or fileio.openNew. These two calls return the size of the file in EAX. If an error occurs, these functions return -1 ($FFFF_FFFF) in EAX. Note that the files must be less than four gigabytes in length when using this function (if you need to check the size of larger files, you will have to call the appropriate OS function rather than these functions; however, since files larger than four gigabytes are rather rare, you probably won't have to worry about this problem).
One interesting use for this function is to determine the number of records in a fixed-length-record random access file. By getting the size of the file and dividing by the size of a record, you can determine the number of records in the file.
Another use for this function is to allow you to determine the size of a (smaller) file, allocate sufficient storage to hold the entire file in memory (by using malloc), and then read the entire file into memory using the fileio.read function. This is generally the fastest way to read data from a file into memory.
Program 7.10 demonstrates the use of the two forms of the fileio.size function by displaying the size of the "myfile.txt" file created by other sample programs in this chapter.program FileSizeDemo; #include( "stdlib.hhf" ) static handle:dword; begin FileSizeDemo; // Display the size of the "FileSizeDemo.hla" file: fileio.size( "FileSizeDemo.hla" ); if( eax <> -1 ) then stdout.put( "Size of file: ", (type uns32 eax), nl ); else stdout.put( "Error calculating file size" nl ); endif; // Same thing, using the file handle as a parameter: fileio.open( "FileSizeDemo.hla", fileio.r ); mov( eax, handle ); fileio.size( handle ); if( eax <> -1 ) then stdout.put( "Size of file(2): ", (type uns32 eax), nl ); else stdout.put( "Error calculating file size" nl ); endif; fileio.close( handle ); end FileSizeDemo; Program 7.10 Sample Program That Demonstrates the fileio.size Function
7.7.3 Deleting Files
Another useful file utility function is the fileio.delete function. As its name suggests, this function deletes a file that you specify as the function's parameter. The calling sequence for this function isfileio.delete( filenameToDelete );
The single parameter is a string containing the pathname of the file you wish to delete. This function returns true/false in the EAX register to denote success/failure.
Program 7.11 provides an example of the use of the fileio.delete function.program DeleteFileDemo; #include( "stdlib.hhf" ) static handle:dword; begin DeleteFileDemo; // Delete the "myfile.txt" file: fileio.delete( "xyz" ); if( eax ) then stdout.put( "Deleted the file", nl ); else stdout.put( "Error deleting the file" nl ); endif; end DeleteFileDemo; Program 7.11 Example Usage of the fileio.delete Procedure
7.8 Directory Operations
In addition to manipulating files, you can also manipulate directories with some of the fileio functions. The HLA Standard Library includes several functions that let you create and use subdirectories. These functions are fileio.cd (change directory), fileio.gwd (get working directory), and fileio.mkdir (make directory). Their calling sequences arefileio.cd( pathnameString ); fileio.gwd( stringToHoldPathname ); fileio.mkdir( newDirectoryName );
The fileio.cd and fileio.mkdir functions return success or failure (true or false, respectively) in the EAX register. For the fileio.gwd function, the string parameter is a destination string where the system will store the pathname to the current directory. You must allocate sufficient storage for the string prior to passing the string to this function (260 characters2 is a good default amount if you're unsure how long the pathname could be). If the actual pathname is too long to fit in the destination string you supply as a parameter, the fileio.gwd function will raise the ex.StringOverflow exception.
The fileio.cd function sets the current working directory to the pathname you specify. After calling this function, the OS will assume that all future "unadorned" file references (those without any "\" or "/" characters in the pathname) will default to the directory you specify as the fileio.cd parameter. Proper use of this function can help make your program much more convenient to use by your program's users since they won't have to enter full pathnames for every file they manipulate.
The fileio.gwd function lets you query the system to determine the current working directory. After a call to fileio.cd, the string that fileio.gwd returns should be the same as fileio.cd's parameter. Typically, you would use this function to keep track of the default directory when your program first starts running. You program will exhibit good manners by switching back to this default directory when your program terminates.
The fileio.mkdir function lets your program create a new subdirectory. If your program creates data files and stores them in a default directory somewhere, it's good etiquette to let the user specify the subdirectory where your program should put these files. If you do this, you should give your users the option to create a new directory (in case they want the data placed in a brand-new directory). You can use fileio.mkdir for this purpose.
7.9 Putting It All Together
This chapter began with a discussion of the basic file operations. That section was rather short because you've already learned most of what you need to know about file I/O when learning the stdout and stdin functions. So the introductory material concentrated on a file general file concepts (like the differences between sequential and random access files and the differences between binary and text files). After teaching you the few extra routines you need in order to open and close files, the remainder of this chapter simply concentrated on providing a few examples (like ISAM) of file access and a discussion of the fileio routines available in the HLA Standard Library.
While this chapter demonstrates the mechanics of file I/O, how you efficiently use files is well beyond the scope of this chapter. In future volumes you will see how to search for data in files, sort data in files, and even create databases. So keep on reading if you're interested in more information about file operations.
1Sorry, the fileio.copy function is not available under Linux.
2This is the default MAX_PATH value in Windows. This is probably sufficient for most Linux applications, too.