TOP | UP: Data Manipulation | NEXT: Resources |
Frontier can read data from and write data to a file on disk. The most common use of this facility is to read or write text, but the data can be of any type. The value that you receive from Frontier when you perform a read, or that you give Frontier when you perform a write, is a binary. If data read from a file is to be processed as text, it must be coerced to a string. (On binaries, see Chapter 10, Datatypes.)
The ability to read and write datafiles lies at the heart of some of Frontier's most powerful applications. In Frontier's Web site management facilities, for example, "rendering" a Web page consists of constructing the HTML for that page internally and then writing the result out to disk as a textfile. The Web site management facilities can also store GIF and other image files in the database and write them out as files to disk as part of the site.
This chapter discusses the UserTalk verbs and techniques for working with datafiles. On file pathnames, and for other verbs dealing with files and folders, see Chapter 31, Driving the System. For verbs dealing with a file's resource fork, see Chapter 20, Resources.
Most often, what you'll want to do is read or write an entire file at once. Utility scripts are provided for this.
To read all of a file's data from disk into a Frontier object:
toys.readWholeFile (pathname)
The result is a binary of the file's entire contents.
So, for instance, here is a script to let the user choose and read a textfile into a Frontier wptext.
The string() coercion is unnecessary here, because wp.settext() will cause implicit coercion of its parameter to a string; it is included in the example just to remind you that the result of toys.readWholeFile() is a binary, not a string. Most of Example 19-1 is unnecessary; reading a textfile into a wptext object is so common that a utility script is provided for that, too.
To read all of a file's data from disk into a wptext object:
toys.readFileIntoTextObject (pathname, addrDestination)
The object is created if it doesn't exist, and replaced if it does.
So we can rewrite Example 19-1 as follows.
local (thePath) |
if file.getfiledialog("Choose a textfile, please.", @thePath, 'TEXT') |
toys.readFileIntoTextObject (thePath, @workspace.temptext) |
To write a Frontier object out to disk as a file:
toys.writeWholeFile (pathname, data, type, creator, date)
Here, for example, is a script that makes a Microsoft Word textfile listing every file in a folder that the user chooses.
To see that there's nothing special about dealing with a file whose data is not text, examine html.loadImageFile() and html.data.standardMacros.image-Ref(). In loadImageFile(), notice these two lines:
adrtable^.[name] = toys.readWholeFile (f)
setBinaryType (@adrtable^.[name], type)
The first line reads the data from the whole file directly into a database entry, which is therefore automatically of binary type; nothing else needs to be done. The binary's internal type is set purely as a way of labelling it, so that image-Ref() can later consult that label to decide what extension and type to give the file when it writes the database entry back out to disk.
The verbs we have looked at so far are utility scripts. Now we come to the more basic verbs upon which those utilities depend. You can use these for finer control as you read or write file data.
Before you can read from or write to a file, the file must exist, and it must be explicitly opened for access; when reading or writing is over, the file must be explicitly closed.1 Let's call the period between opening and closing a file a session. Then during any session, the first read begins at the beginning and each successive read starts right after where the previous read ended, but every write appends to the end of the file. There is no problem with mixing reads and writes in a session.
The operating system will complain if certain file access problems occur, and Frontier will raise an error. It is wise, therefore, to embed sessions in a try, because if something goes wrong during a session, it is crucial that you close the file.
To create an empty file (replacing an existing file):
file.new (pathname)
To open a file for read/write access, beginning a session:
file.open (pathname)
To close a file previously opened for read/write access, ending a session:
file.close (pathname)
file.size (pathname)
See also Chapter 31, Driving the System, where file.size() is discussed again. On Mac OS, this verb reports the combined size of the resource fork and the data fork; hence, it can be used to learn the number of bytes of data only if you know that there is no resource fork. You do know this if Frontier created the file (and never wrote to the resource fork). When in doubt, read the whole file and examine the size of the result; this works because reading a file reads its data fork only.
To read from a previously opened file:
file.read (pathname, howManyCharacters)
An interesting problem is that although successive reads within a session move the "pointer" at which the next read will begin, there is no way to ask Frontier where the pointer actually is. But you might need to know this, because if howManyCharacters is too large, the read may fail, and you won't be able to do any more reading in this session. One solution is to determine the size of the data beforehand, and then keep track of the number of characters read.
Similarly, you cannot ask Frontier to set the pointer just anywhere; for so-called "random access" reading, you have to begin a new session and then read up to the point where you actually want to start reading.
It is often simpler just to read the whole file and then process the result. For instance, here is a way of learning the last character of a textfile:
local (s = toys.readWholeFile("HD:textfile"))
msg(char(s[sizeof(s)]))
To read from a previously opened file until an end-of-line (cr) is reached:
file.readLine (pathname)
To check whether this session's reading has reached the end of the file:
file.endOfFile (pathname)
To append to the end of a file:
file.write (pathname, whatToWrite)
To append a string plus cr to the end of a file:
file.writeLine (pathname, whatToWrite)
As with reading, so with writing you cannot set the pointer just anywhere; your only choice is to append to the end of a file. To insert material at the beginning or middle of a file, you need to overwrite the entire file. For an example, see suites.samples.NewIn96.add2lines() , which inserts two lines at the start of a textfile by reading in the file as a string, inserting the lines at the start of the string, and overwriting the file with the result.
A couple of verbs help look for text on disk without opening any files.
To learn whether a file contains a certain string:
file.findInFile (pathname, whatToLookFor)
To learn what file in a certain folder contains a certain string:
file.findInFolder (folderPathname, depth, whatToLookFor)
file.findInFolder() is a utility script that calls file.findInFile() in a fileloop(). You learn nothing from file.findInFile() about where in the file the string appears (to learn that, you could read the file and then use string.patternmatch()); but it is quick and convenient.
To determine the number of lines in a file:
file.countLines (pathname)
The result of file.countLines() is not necessarily the same as the number of calls to file.readLine() that can be successfully performed, because it just counts end-of-lines, and the file might not end with an end-of-line.
TOP | UP: Data Manipulation | NEXT: Resources |