Previous section   Next section

Practical Programming in Tcl & Tk, Third Edition
By Brent B. Welch

Table of Contents
Chapter 9.  Working with Files and Programs

Reading and Writing

The standard I/O channels are already open for you. There is a standard input channel, a standard output channel, and a standard error output channel. These channels are identified by stdin, stdout, and stderr, respectively. Other I/O channels are returned by the open command, and by the socket command described on page 228.

There may be cases when the standard I/O channels are not available. Windows has no standard error channel. Some UNIX window managers close the standard I/O channels when you start programs from window manager menus. You can also close the standard I/O channels with close.

The puts and gets Commands

The puts command writes a string and a newline to the output channel. There are a couple of details about the puts command that we have not yet used. It takes a -nonewline argument that prevents the newline character that is normally appended to the output channel. This is used in the prompt example below. The second feature is that the channel identifier is optional, defaulting to stdout if not specified. Note that you must use flush to force output of a partial line. This is illustrated in Example 9-7.

Example 9-7 Prompting for input.
puts -nonewline "Enter value: "
flush stdout ;# Necessary to get partial line output
set answer [gets stdin]

The gets command reads a line of input, and it has two forms. In the previous example, with just a single argument, gets returns the line read from the specified I/O channel. It discards the trailing newline from the return value. If end of file is reached, an empty string is returned. You must use the eof command to tell the difference between a blank line and end-of-file. eof returns 1 if there is end of file. Given a second varName argument, gets stores the line into a named variable and returns the number of bytes read. It discards the trailing newline, which is not counted. A -1 is returned if the channel has reached the end of file.

Example 9-8 A read loop using gets.
while {[gets $channel line] >= 0} {
   # Process line
close $channel

The read Command

The read command reads blocks of data, and this capability is often more efficient. There are two forms for read: You can specify the -nonewline argument or the numBytes argument, but not both. Without numBytes, the whole file (or what is left in the I/O channel) is read and returned. The -nonewline argument causes the trailing newline to be discarded. Given a byte count argument, read returns that amount, or less if there is not enough data in the channel. The trailing newline is not discarded in this case.

Example 9-9 A read loop using read and split.
foreach line [split [read $channel] \n] {
   # Process line
close $channel

For moderate-sized files, it is about 10 percent faster to loop over the lines in a file using the read loop in the second example. In this case, read returns the whole file, and split chops the file into list elements, one for each line. For small files (less than 1K) it doesn't really matter. For large files (megabytes) you might induce paging with this approach.

Platform-Specific End of Line Characters

Tcl automatically detects different end of line conventions. On UNIX, text lines are ended with a newline character (\n). On Macintosh, they are terminated with a carriage return (\r). On Windows, they are terminated with a carriage return, newline sequence (\r\n). Tcl accepts any of these, and the line terminator can even change within a file. All these different conventions are converted to the UNIX style so that once read, text lines are always terminated with a newline character (\n). Both the read and gets commands do this conversion.

During output, text lines are generated in the platform-native format. The automatic handling of line formats means that it is easy to convert a file to native format. You just need to read it in and write it out:

puts -nonewline $out [read $in]

To suppress conversions, use the fconfigure command, which is described in more detail on page 223.

Example 9-10 demonstrates a File_Copy procedure that translates files to native format. It is complicated because it handles directories:

Example 9-10 Copy a file and translate to native format.
proc File_Copy {src dest} {
   if [file isdirectory $src] {
      file mkdir $dest
      foreach f [glob -nocomplain [file join $src *]] {
         File_Copy $f [file join $dest [file tail $f]]
   if [file isdirectory $dest] {
      set dest [file join $dest [file tail $src]]
   set in [open $src]
   set out [open $dest w]
   puts -nonewline $out [read $in]
   close $out ; close $in

Random Access I/O

The seek and tell commands provide random access to I/O channels. Each channel has a current position called the seek offset. Each read or write operation updates the seek offset by the number of bytes transferred. The current value of the offset is returned by the tell command. The seek command sets the seek offset by an amount, which can be positive or negative, from an origin which is either start, current, or end.

Closing I/O channels

The close command is just as important as the others because it frees operating system resources associated with the I/O channel. If you forget to close a channel, it will be closed when your process exits. However, if you have a long-running program, like a Tk script, you might exhaust some operating system resources if you forget to close your I/O channels.


The close command can raise an error.

If the channel was a process pipeline and any of the processes wrote to their standard error channel, then Tcl believes this is an error. The error is raised when the channel to the pipeline is finally closed. Similarly, if any of the processes in the pipeline exit with a nonzero status, close raises an error.

      Previous section   Next section