| ActiveTcl User Guide |

# File Access 101

Tcl provides several methods to read from and write to files on disk. The simplest methods to access a file are via `gets` and `puts`. When there is a lot of data to be read, however, it is sometimes more efficient to use the `read` command to load an entire file, and then parse the file into lines with the `split` command.

These methods can also be used for communicating over sockets and pipes. It is even possible, via the so-called virtual file system to use files stored in memory rather than on disk. Tcl provides an almost uniform interface to these very different resources, so that in general you do not need to concern yourself with the details.

`open` `fileName` `?access?` `?permission?`
  Opens a file and returns a token to be used when accessing the file via `gets`, `puts`, `close`, etc.
  - `FileName` is the name of the file to open.
  - `access` is the file access mode
    - `r`......Open the file for reading. The file must already exist.
    - `r+`...Open the file for reading and writing. The file must already exist.
    - `w`.....Open the file for writing. Create the file if it doesn't exist, or set the length to zero if it does exist.
    - `w+`..Open the file for reading and writing. Create the file if it doesn't exist, or set the length to zero if it does exist.
    - `a`......Open the file for writing. The file must already exist. Set the current location to the end of the file.
    - `a+`...Open the file for writing. The file does not exist, create it. Set the current location to the end of the file.
  - `permission` is an integer to use to set the file access permissions. The default is `rw-rw-rw-` (0666). You can use it to set the permissions for the file's owner, the group he/she belongs to and for all the other users. For many applications, the default is fine.
`close` `fileID`
  Closes a file previously opened with `open`, and flushes any remaining output.
`gets` `fileID ?varName?`

  Reads a line of input from `FileID`, and discards the terminating newline.

  If there is a `varName` argument, `gets` returns the number of characters read (or -1 if an EOF occurs), and places the line of input in `varName`.

  If `varName` is not specified, `gets` returns the line of input. An empty string will be returned if:

  - There is a blank line in the file.
  - The current location is at the end of the file. (An EOF occurs.)
`puts ?-nonewline?` `?fileID? string`
  Writes the characters in string to the stream referenced by `fileID`, where `fileID` is one of:
  - The value returned by a previous call to `open` with write access.
  - `stdout`
  - `stderr`
`read` `?-nonewline?` `fileID`
  Reads all the remaining bytes from `fileID`, and returns that string. If `-nonewline` is set, then the last character will be discarded if it is a newline. Any existing end of file condition is cleared before the `read` command is executed.
`read` `fileID numBytes`
  Reads up to `numBytes` from `fileID`, and returns the input as a Tcl string. Any existing end of file condition is cleared before the `read` command is executed.
`seek` `fileID` `offset` `?origin?`
  Change the current position within the file referenced by `fileID`. Note that if the file was opened with "a" `access` that the current position can not be set before the end of the file for writing, but can be set to the beginning of the file for reading.
  - `fileID` is one of:
    - a File identifier returned by `open`
    - `stdin`
    - `stdout`
    - `stderr`
  - `offset` is the offset in bytes at which the current position is to be set. The position from which the offset is measured defaults to the start of the file, but can be from the current location, or the end by setting `origin` appropriately.
  - `origin` is the position to measure `offset` from. It defaults to the start of the file. `Origin` must be one of:
    - `start`.........`Offset` is measured from the start of the file.
    - `current`...`Offset` is measured from the current position in the file.
    - `end`...........`Offset` is measured from the end of the file.
`tell` `fileID`
  Returns the position of the access pointer in `fileID` as a decimal string.
`flush` `fileID`
  Flushes any output that has been buffered for `fileID`.
`eof` `fileID`
  returns 1 if an End Of File condition exists, otherwise returns 0.

Points to remember about Tcl file access:

- The file I/O is buffered. The output may not be sent out when you expect it to be sent. Files will all be closed and flushed when your program exits normally, but may only be closed (not flushed) if the program is terminated in an unexpected manner.
- There are a finite number of open file slots available. If you expect the program to run in a manner that will cause it to open several files, remember to close the files when you are done with them.
- An empty line is indistinguishable from an EOF with the command:

  > `set string [gets filename]`

  Use the `eof` command to determine if the file is at the end or use the other form of `gets` (see the example).
- You can't overwrite any data in a file that was opened with `a` (append) `access`. You can, however seek to the beginning of the file for `gets` commands.
- Opening a file with the `w+` access will allow you to overwrite data, but will delete all existing data in the file.
- Opening a file with the `r+` access will allow you to overwrite data, while saving the existing data in the file.
- By default the commands assume that strings represent "readable" text. If you want to read "binary" data, you will have to use the `fconfigure` command.
- Often, especially if you deal with configuration data for your programs, you can use the `source` command instead of the relatively low-level commands presented here. Just make sure your data can be interpreted as Tcl commands and "source" the file.

## Example

    #
    # Count the number of lines in a text file
    #
    set infile [open "myfile.txt" r]
    set number 0

    #
    # gets with two arguments returns the length of the line,
    # -1 if the end of the file is found
    #
    while { [gets $infile line] >= 0 } {
        incr number
    }
    close $infile

    puts "Number of lines: $number"

    #
    # Also report it in an external file
    #
    set outfile [open "report.out" w]
    puts $outfile "Number of lines: $number"
    close $outfile