Slurping Up Text in TCL

One common Tcl-ism is to read an entire data file into memory in one fell swoop, instead of reading and processing each line in turn. This works well if your file is small compared to system memory, and you can afford to do all the processing in memory. You can also use the split command to break the file down into a list of individual lines, like this:

set fp [open myFile r]
set data [split [read $fp] "\n"]
close $fp

More details on efficiency can be found on the Tcl Performance page.


[email protected]:

I am not sure what I am missing with the problem I am trying to solve.

I am using Tcl version 8.3 on various platforms(Linux, Mac).

I am in the midst of building a small set of utilities to help me generate HTML (either with embedded ADP tags or simply static pages), and hope to eventually build a small UI on top of it with Tk.

What I want is simply a snippet of code that will let me take in a raw text file, stick it into a variable, then let me write it to a file.

The basic idea is this: I use my utils to process all the headers and footers and Javascript stuff in Tcl, then just include_raw {somefile} and then send the results to a file.

What am I missing? How do I tell Tcl to not evaluate what it reads from a file?

this is my code right now....

proc rawInclude {raw_file} {
    if {[catch {set fd [open $raw_file r]} err]}{
        return
    }
    set bytes_read [read $fd]
    close $fd
    return $bytes_read
}

DKF: This looks about right to me (though Tcl Performance covers some tips on how to make this faster, it still looks like it should work.) If you're having problems with this, then perhaps you should look at where you are using it...


[email protected]: In looking into this, I get the feeling that I should be using fcopy instead.

Using fcopy ensures that no translation or attempt to use the input as variables or commands occurs. Will check this out further.

Once I get this done, I expect that I will (eventually) post my code, in full. The code will let you automate HTML file production, including JavaScript, mouseovers, etc.


OK, now I feel stupid.

When I changed things to use fcopy I did not realize at first that the first condition in the IF stmt was always true, so it was returning without doing anything to the file.


Mike Tuxford: Correct. You were catch'ing the result for the set and not the open. Should be...

if {[catch {open $raw_file r} fd]} {
    # $fd is your error message instead of File Descriptor
    puts $fd
    return
}
# $fd is your File Descriptor

RS: notes that catch catches everything inside body, not only the outermost error.

Mike Tuxford: Interesting. Is there any advantage to this? It appears to me that it's just another step added but we don't always see how that can be useful. The method I showed has become such SOP for file opening for me.


DKF: A variation is this:

proc readFile {filename args} {
    set fd [open $filename] ;# I'm just letting errors propagate...
    # Pass extra arguments through to [fconfigure]
    if {[llength $args]} {
        eval [list fconfigure $fd] $args
    }
    set data [read $fd]
    close $fd
    return $data
}

dbohdan 2014-07-14: Current Tcllib implements the procedure fileutil::cat , which can slurp up any number of files and return their concatenated content. It allows you to set separate fconfigure options for each individual file.


mpettigr 2016-09-12: Here's a one liner...

expr {([set fileContents [read [set __fh [open "fileName"]]]] != {}) && [close $__fh] == {} && [unset __fh] == {}}

Here's a couple more:

set fileContents [try {open fileName} on ok f {read $f} finally {close $f ; unset f}]
set fileContents [apply {n {apply {f {apply {{r c} {set r}} [read $f] [close $f]}} [open $n]}} fileName]

LEG proposes inlined K: on entry c contains fileName, when leaving fileContents - cheating 14 chars away.

set c [read [set c [open $c]]][close $c]