This is the new home of the egghelp.org community forum.
All data has been migrated (including user logins/passwords) to a new phpBB version.


For more information, see this announcement post. Click the X in the top right-corner of this box to dismiss this message.

read file

Old posts that have not been replied to for several years.
Locked
O
Ofloo
Owner
Posts: 953
Joined: Tue May 13, 2003 1:37 am
Location: Belguim
Contact:

read file

Post by Ofloo »

how do i read only the last line of a file ??

or beter yet how do i get the last line of a file (txt)
XplaiN but think of me as stupid
User avatar
stdragon
Owner
Posts: 959
Joined: Sun Sep 23, 2001 8:00 pm
Contact:

Post by stdragon »

I didn't test this, but I think it will work pretty well:

Code: Select all

proc read_last_line {fname} {
  set fp [open $fname r]
  set pos 512
  while {1} {
    seek $fp -$pos end
    set data [read -nonewline $fp]
    set lines [split $data \n]
    if {[llength $lines] > 1} {
      close $fp
      return [lindex $lines end]
    } elseif {[tell $fp] >= $pos} {
      close $fp
      return $data
    }
    incr pos 512
  }
}
O
Ofloo
Owner
Posts: 953
Joined: Tue May 13, 2003 1:37 am
Location: Belguim
Contact:

Post by Ofloo »

i managed to make it work some how and am verry greatfull for what you wrote, but could you tel me what you did or xplain it little
XplaiN but think of me as stupid
User avatar
stdragon
Owner
Posts: 959
Joined: Sun Sep 23, 2001 8:00 pm
Contact:

Post by stdragon »

Okay let me add some comments:

Code: Select all

proc read_last_line {fname} {
  set fp [open $fname r]

  # pos is the position in the file (in reverse)
  # we start at 512 bytes from the end of the file
  set pos 512
  while {1} {
    # this moves to $pos bytes before the end of the file
    seek $fp -$pos end
    # read to the end of the file
    set data [read -nonewline $fp]
    # we may have read multiple lines (if less than 512 bytes)
    set lines [split $data \n]
    if {[llength $lines] > 1} {
      # yup, we read at least 1 line and part of another, so we know
      # we for sure have the complete last line, we're done
      close $fp
      return [lindex $lines end]
    } elseif {[tell $fp] < $pos} {
      # Well, before I had >=, but it should be <.
      # This happens if the entire file is < 512 bytes, and we didn't read
      # in multiple lines... therefore the file is a single line and we read the
      # whole thing, so we're done.
      close $fp
      return $data
    }
    # Now we just repeat the process, adding another 512 bytes
    incr pos 512
  }
}
Well, the basic method is like.... imagine you have a 10 meg text file (quotes, logs, whatever). You only want the last line. Well, usually lines are less than 512 bytes. So what we do is use "seek" to skip to 512 bytes before the end of the file. It does this without reading in all the bytes between, so it's much more efficient.

So, now no matter how big the file is, we are 512 bytes from the end. So we read in the end of the file and see if it's a complete line. The way you tell if it's a complete line is to see if it has a \n inside it. E.g. a partial line plus a complete next line. The last line in the split array will be the last line in the file, and the partial ones are ignored.

If there is no partial line, then it's possible that the line is longer than 512 bytes. I mean, if you have a line that's 3k long, and we only read in the last 512 bytes, there won't be a \n in the middle right? Because the last \n is 3k from the end of the file. So, if we don't have an embedded \n, we repeat the whole process with another 512 bytes. So 512 the first time, then 1k, then 1.5k, then 2k, etc. Most lines are < 512 bytes though so it probably won't ever happen.

The only other thing we have to be careful of is if the file is only one line. Then we will *never* read a partial line, because there is only one. So we check for that by seeing whether [tell $fp] is < $pos. [tell] returns the file byte position. So at the end of the file (after [read]), if the byte position is less than $pos, we know we've read in the whole file. Why? Well, say the file is 300 bytes. We first did [seek $fp end-512], which basically puts us back at the beginning (you can't seek backwards past the start). Then we use [read] to read to the end. But since it's only a 300 byte file, [tell] will return 300. Since 300 < 512, we know we've read the entire file. That means that if there isn't a partial line, the entire file is one line and we're done.

Well, that was probably more detail than you wanted. But there you go.
O
Ofloo
Owner
Posts: 953
Joined: Tue May 13, 2003 1:37 am
Location: Belguim
Contact:

Post by Ofloo »

omg great
XplaiN but think of me as stupid
Locked