Discussion:
Read file backwards?
(too old to reply)
Monkey With A Gun
2003-08-07 01:54:33 UTC
Permalink
I'm trying to read a file line-by-line but in reverse order (ie, last line
first), and have some success using...

set fid [open "|tail -r $file" r]

while {![eof $fid]} {
puts [gets $fid]
}

Unfortunately, "tail -r" isn't very portable, so I was wondering if anyone
could give me pointers on how to accomplish the same in pure Tcl?

The only other solution that's struck me is to use a combination of seek,
tell and read to inch my way backwards through the file one char at a time
looking for newlines, but that seems very inefficient,

TIA.
Mark G. Saye
2003-08-07 02:23:13 UTC
Permalink
Post by Monkey With A Gun
I'm trying to read a file line-by-line but in reverse order (ie, last line
first), and have some success using...
set fid [open "|tail -r $file" r]
while {![eof $fid]} {
puts [gets $fid]
}
Unfortunately, "tail -r" isn't very portable, so I was wondering if anyone
could give me pointers on how to accomplish the same in pure Tcl?
The only other solution that's struck me is to use a combination of seek,
tell and read to inch my way backwards through the file one char at a time
looking for newlines, but that seems very inefficient,
Hey Monkey With A Gun,

How about something like this? Open the file and store the offset of
each new line. then seek to required offset and read line

Using a sample file called 'lines.txt' containing the text:

This is line 1
This is line 2
This is line 3
This is line 4
This is line 5

and then:

set file lines.txt

if { [catch {open $file r} fd] } {
puts "$fd"
} else {
set list [list [tell $fd]]
while { [gets $fd line] > -1 } {
lappend list [tell $fd]
}
puts "offset list = \[$list\]"

set n [llength $list]
for {set i [expr {$n-1}]} {$i > 0} {incr i -1} {
seek $fd [lindex $list [expr {$i-1}]] start
gets $fd line
puts "line $i = \[$line\]"
}
close $fd
}

produces:

offset list = [0 15 30 45 60 75]
line 5 = [This is line 5]
line 4 = [This is line 4]
line 3 = [This is line 3]
line 2 = [This is line 2]
line 1 = [This is line 1]

Is this any use to you?
--
Mark G. Saye
markgsaye @ yahoo.com
Mike Tuxford
2003-08-07 02:40:04 UTC
Permalink
Monkey With A Gun <***@ng.tcl> wrote:
:I'm trying to read a file line-by-line but in reverse order (ie, last line
:first), and have some success using...
:
: set fid [open "|tail -r $file" r]
:
: while {![eof $fid]} {
: puts [gets $fid]
: }
:
:Unfortunately, "tail -r" isn't very portable, so I was wondering if anyone
:could give me pointers on how to accomplish the same in pure Tcl?
:
:The only other solution that's struck me is to use a combination of seek,
:tell and read to inch my way backwards through the file one char at a time
:looking for newlines, but that seems very inefficient,
:
:TIA.

Cute ***@host ... Does this fit your needs?

set filename "yourfile.txt"
if {[catch {open $filename r} fd]} {
# trapp error and exit
puts "error=$fd"
exit
} else {
# read into a list, stripping the newline char.
set data [split [read $fd] \n]
close $fd
}

# step through the list backwards
# "-2" accounts for the extra EOF data line
for {set i [expr [llength $data] -2]} {$i >= 0} {incr i -1} {
puts [lindex $data $i]
}
--
_ _ ____
//\/\ike ||uxford
***@earthlink.net
Gary Petersen
2003-08-08 00:25:07 UTC
Permalink
On Wed, 06 Aug 2003 20:54:33 -0500, in message
Post by Monkey With A Gun
I'm trying to read a file line-by-line but in reverse order (ie, last
line first), and have some success using...
set fid [open "|tail -r $file" r]
while {![eof $fid]} {
puts [gets $fid]
}
}
Unfortunately, "tail -r" isn't very portable, so I was wondering if
anyone could give me pointers on how to accomplish the same in pure Tcl?
The only other solution that's struck me is to use a combination of
seek, tell and read to inch my way backwards through the file one char
at a time looking for newlines, but that seems very inefficient,
TIA.
If the file fits easily into memory, I would do
this:

proc rrev {fname} {
set fid [open $fname]
set arr {}
while {! [eof $fid]} {
set arr [linsert $arr 0 [gets $fid]]
}
close $fid
return $arr
}

set thearray [rrev [lindex $argv 0]]

foreach var $thearray {
puts "process: $var"
}

HTH

Loading...