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. Open an ASCII text file for input
  2. Open a file for output
  3. Read input file until string matches (or some other condition is met)
  4. Write some lines from input (modified or not) to output
  5. Close 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?