Next: , Previous: Floating Point Tutorial, Up: Tutorial


3.27 Files

This section gives a short introduction into how to use files inside Forth. It's broken up into five easy steps:

  1. Opened an ASCII text file for input
  2. Opened a file for output
  3. Read input file until string matched (or some other condition matched)
  4. Wrote some lines from input ( modified or not) to output
  5. Closed the files.

Reference: General files.

3.27.1 Open file for input

     s" foo.in"  r/o open-file throw Value fd-in

3.27.2 Create file for output

     s" foo.out" w/o create-file throw Value fd-out

The available file modes are r/o for read-only access, r/w for read-write access, and w/o for write-only access. You could open both files with r/w, too, if you like. All file words return error codes; for most applications, it's best to pass there error codes with throw to the outer error handler.

If you want words for opening and assigning, define them as follows:

     0 Value fd-in
     0 Value fd-out
     : open-input ( addr u -- )  r/o open-file throw to fd-in ;
     : open-output ( addr u -- )  w/o create-file throw to fd-out ;

Usage example:

     s" foo.in" open-input
     s" foo.out" open-output

3.27.3 Scan file for a particular line

     256 Constant max-line
     Create line-buffer  max-line 2 + allot
     
     : scan-file ( addr u -- )
       begin
           line-buffer max-line fd-in read-line throw
       while
              >r 2dup line-buffer r> compare 0=
          until
       else
          drop
       then
       2drop ;

read-line ( addr u1 fd -- u2 flag ior ) reads up to u1 bytes into the buffer at addr, and returns the number of bytes read, a flag that is false when the end of file is reached, and an error code.

compare ( addr1 u1 addr2 u2 -- n ) compares two strings and returns zero if both strings are equal. It returns a positive number if the first string is lexically greater, a negative if the second string is lexically greater.

We haven't seen this loop here; it has two exits. Since the while exits with the number of bytes read on the stack, we have to clean up that separately; that's after the else.

Usage example:

     s" The text I search is here" scan-file

3.27.4 Copy input to output

     : copy-file ( -- )
       begin
           line-buffer max-line fd-in read-line throw
       while
           line-buffer swap fd-out write-line throw
       repeat
       drop ;

3.27.5 Close files

     fd-in close-file throw
     fd-out close-file throw

Likewise, you can put that into definitions, too:

     : close-input ( -- )  fd-in close-file throw ;
     : close-output ( -- )  fd-out close-file throw ;
Assignment: How could you modify copy-file so that it copies until a second line is matched? Can you write a program that extracts a section of a text file, given the line that starts and the line that terminates that section?