The FileTools Package
The FileTools package provides an interface for file input and output.
|
Organization
|
|
The FileTools package contains commands and three subpackages, Text, Binary, and Compressed. The Text subpackage contains commands for manipulating files that must be interpreted as textual data (that is, ASCII encoded text). The Binary package contains commands that can be used to manipulate binary data. The commands currently in the Binary subpackage allow for reading and writing hardware data types. The Compressed package contains commands for manipulating compressed files (gzip format).
|
|
FileTools Commands
|
|
The commands in the top-level FileTools package do not provide access to the contents of a file. Instead, they provide information about a file and allow you to manipulate files in the file system. Some of the actions you can perform are listed here:
For a complete list of commands, see FileTools.
|
|
FileTools[Text] Commands
|
|
The Text subpackage contains commands for reading and writing data to files as text. You can perform the following tasks.
|
|
FileTools[Binary] Commands
|
|
The Binary subpackage allows hardware data types to be written to disk in various formats. You can perform the following tasks.
•
|
Write data to disk by calling Write and read from disk by calling Read.
|
•
|
Query the number of bytes of data available from the file by calling CountBytes.
|
|
|
FileTools[Compressed] Commands
|
|
The Compressed package contains commands for manipulating compressed files. You can perform the following tasks.
•
|
Open and close files by calling Open and Close.
|
|
|
Examples of Selected Topics
|
|
Important: The following examples create files in the current directory. Therefore, you must have read and write access to this directory before attempting to execute this worksheet. If you do not have read and write access to the directory returned by this call to currentdir(), you must set the current directory to one in which you can create files. To do this, add the path as an argument to currentdir( "/path/to/directory" );
|
Basic Reading and Writing of Text Data
|
|
Select a filename. TemporaryFilename returns a filename that is not in use, with its string argument as a prefix to the filename.
>
|
filename := TemporaryFilename( "FTex" );
|
WriteLine returns the number of Maple characters written to the file. Notice that because different operating systems may encode text differently from Maple the number of characters returned may not be the same as the number of bytes in the file.
>
|
Text[WriteLine]( filename, "FileTools example" );
|
>
|
Text[Close]( filename );
|
>
|
Text[ReadLine]( filename );
|
>
|
Text[Close]( filename );
|
When creating a new file, it is not necessary to explicitly open the file. However, by default a file is overwritten.
>
|
Text[WriteLine]( filename, "More data" );
|
>
|
Text[Close]( filename );
|
>
|
Text[ReadLine]( filename );
|
>
|
Text[ReadCharacter]( filename );
|
>
|
AtEndOfFile( filename );
|
>
|
Text[Close]( filename );
|
To avoid this, specify append=true.
>
|
Text[Open]( filename, append = true );
|
>
|
Text[WriteLine]( filename, "Even more data" );
|
>
|
Text[Close]( filename );
|
>
|
Text[ReadLine]( filename );
|
>
|
Text[ReadLine]( filename );
|
Using Open, you can specify not to create a file when opening one that does not exist.
>
|
Text[Open]( filename, create=false );
|
Write some numbers.
>
|
for i from 1 to 10 do
Text[WriteInteger]( filename, i );
end do:
|
Each open file maintains a reference that indicates where in the file an operation (a read or write) occurs. This is sometimes referred to as the file position. Instead of closing and reopening the file, move the file position to the beginning of the file.
>
|
Position( filename, 0 ):
|
Using integers:
>
|
Text[CountIntegers]( filename );
|
>
|
Text[ReadInteger]( filename );
|
The result may not be what you expected. What happened?
>
|
Position( filename, 0 ):
|
>
|
Text[ReadLine]( filename );
|
Without a space between the integers they are interpreted as one long integer. WriteInteger and WriteFloat have the delim option for automatically inserting characters. If you do not want delimiters on both sides, use leftdelim or rightdelim instead.
>
|
for i from 1 to 10 do
Text[WriteInteger]( filename, i, delim=" " );
end do:
|
>
|
Position( filename, 0 ):
|
>
|
Text[CountIntegers]( filename );
|
>
|
Text[ReadLine]( filename );
|
>
|
for i from 1 to 10 do
Text[WriteInteger]( filename, i, leftdelim=" | " );
end do:
|
>
|
Position( filename, 0 ):
|
>
|
Text[ReadLine]( filename );
|
>
|
for i from 1 to 10 do
Text[WriteInteger]( filename, i, rightdelim=" | " );
end do:
|
>
|
Position( filename, 0 ):
|
>
|
Text[ReadLine]( filename );
|
Now you can read the integers.
>
|
Position( filename, 0 );
|
>
|
Text[ReadInteger]( filename );
|
>
|
Text[ReadInteger]( filename );
|
No more integers? The file position is immediately after the "1" so the string that ReadInteger sees is "|", which is not an integer. However, FileTools also provides the ReadNextInteger (and ReadNextFloat). This command skips strings that do not form a valid integer until it finds one that does. You can use ReadNextInteger to get all the integers from the file, ignoring the delimiters.
>
|
Text[ReadString]( filename );
|
>
|
Position( filename, 0 );
|
>
|
for i from 1 to 10 do
Text[ReadNextInteger]( filename );
end do;
|
|
|
Basic Reading and Writing of Binary Data
|
|
The Binary subpackage is designed for reading and writing of hardware data types. In particular, it can convert hardware types with different endianness.
>
|
filename := TemporaryFilename( "FTex" );
|
Write can accept data as a list, set, or rtable. When specifying a list or set, the data type must be specified. If an rtable is used, the rtable should be of the same hardware type that is to be written to disk. Valid hardware types are integer[1], integer[2], integer[4], integer[8], float[4], and float[8].
>
|
Binary[Write]( filename, [1,2,3,4,5,6,7], integer[1] );
|
>
|
Position( filename, 0 ):
|
When reading from a binary file, you must specify the type to read. However, specifying the number of objects to read is optional. If it is unspecified, Read attempts to read all the data in from the file.
>
|
Binary[Read]( filename, integer[1], 2 );
|
>
|
Binary[Read]( filename, integer[1] );
|
Using endianness:
>
|
num := 1*256^3+2*256^2+3*256+4;
|
>
|
num2 := 4*256^3+3*256^2+2*256+1;
|
When writing binary data, you can specify what byte ordering you want to use: little, big, native, and network are all recognized options. By default, Maple assumes data is stored in network order. This means that files created using the Binary subpackage on one platform are interpreted properly on any other platform if the defaults are used. When using native byte ordering, Read orders the data according to how the computer represents data. This can be useful when sharing data between Maple and other programs that use data in the native format.
>
|
Binary[Write]( filename, integer[4], [num] );
|
>
|
Binary[Close]( filename );
|
>
|
Binary[Read]( filename, integer[4] );
|
>
|
Binary[Close]( filename );
|
>
|
Binary[Read]( filename, integer[4], byteorder=little );
|
>
|
Binary[Close]( filename );
|
>
|
Binary[Read]( filename, integer[4], byteorder=network );
|
The byteorder=native option assumes the file is created using the endianness of the current computer. Therefore, the result of the next Read depends on the machine on which you are running. On an x86 processor it gives the bytes swapped result. Thus, x86 is a little endian machine.
>
|
Binary[Close]( filename );
|
>
|
Binary[Read]( filename, integer[4], byteorder=native );
|
>
|
Binary[Close]( filename );
|
>
|
Binary[Read]( filename, integer[1], 4 );
|
|
|
File Locking
|
|
Using the FileTools package you can lock sections of a file. Locking a file refers to the ability to tell other programs not to change the file. This can be useful when multiple programs are sharing a single file for data storage. If one program is changing data while another is reading data, it is possible that the reading program will get corrupted data. The Maple file locking mechanism is known as an advisory scheme. In an advisory scheme, programs are not prevented from making changes to files that are locked. However, if they request permission to access the locked file, it is denied. It is important that all programs accessing the file respect the file locking protocol. To this end, Maple uses the file locking mechanisms provided by the different operating systems. On Windows, Maple uses the FileLock functions, defined in the Win32 API. Under UNIX, Maple uses the fcntl style of file locking.
FileTools locks allow subsections of a file to be locked. Therefore, if you are changing only a small section of the file, other programs are able to access other areas of the file at the same time.
Exclusive locks are available on all platforms. An exclusive lock allows only one program to have a lock on a section of a file. However, if multiple programs need only read access to the same section of the file, an exclusive lock may be too severe. Therefore, where supported by the operating system, Maple also allows shared locks. A shared lock states that the program needs to read from a section of a file. Therefore, multiple shared locks on the same area of a file are acceptable. However, if a program needs to write to a section of a file it must obtain an exclusive lock. An exclusive lock is not given if a shared or exclusive lock conflicts. Similarly a shared lock is not given if an exclusive lock conflicts. Currently shared locks are available on all UNIX platforms and Mac OS X.
The basic method for working with files in an advisory locking scheme is the following.
1. Check the file for a lock. If it is locked, then wait for the lock to be released.
2. Try to directly obtain the lock. If you fail to obtain the lock, go to step 1.
3. Access the file.
4. Release the lock.
Note: It is not possible to display all the features of file locking with only one Maple session running. Therefore, you must start a second instance of Maple, (or a second session, if you are running in parallel kernel mode) and try accessing the file from there.
>
|
filename := cat( currentdir(), "/", TemporaryFilename( "FTex" ) );
|
>
|
for i from 10 to 40 do
Text[WriteInteger]( filename, i, rightdelim="\n" );
end do:
|
>
|
Text[Close]( filename );
|
You must first open the file before you lock it. Be careful not to overwrite the existing file. Since you do not have a lock on the file, someone else might. The file can be opened by using Text or Binary.
>
|
Text[Open]( filename, create=false, overwrite=false );
|
By default, the locking functions assume you are trying to lock the whole file.
>
|
IsLockable( filename );
|
Now the lock has been obtained. In a separate Maple session, call IsLockable(). You must open the file.
Now try locking it again. You should be able to lock the file.
Try locking a region of the file. To do this, use the bytes=a..b argument.
IsLockable accepts the same arguments as lock.
>
|
IsLockable( filename, bytes=0..10 );
|
>
|
Lock( filename, bytes=0..10 );
|
Now in the other session, try locking the entire file. It should fail because a subsection of the file is locked. Try locking the range 5..15. This should also fail. However, if you try locking the range 15..25, it should succeed. It is possible for a single process to hold multiple locks into the same file.
>
|
IsLockable( filename, bytes=30..40 );
|
>
|
Lock( filename, bytes=30..40 );
|
You now have a lock on the ranges 0..10 and 30..40.
>
|
Unlock( filename, bytes=30..40 );
|
>
|
Unlock( filename, bytes=0..10 );
|
|
|
Return to Index for Example Worksheets
|