tags:

views:

94

answers:

4

I have a text file which contains a time stamp on each line. My goal is to find the time range. All the times are in order so the first line will be the earliest time and the last line will be the latest time. I only need the very first and very last line. What would be the most efficient way to get these lines in python.

Note: These files are relatively large in length, about 1-2 million lines each and I have to do this for several hundred files.

+3  A: 

docs for io module

with open(fname, 'rb') as fh:
    first = next(fh).decode()

    fh.seek(-1024, 2)
    last = fh.readlines()[-1].decode()

The variable value here is 1024: it represents the average string length. I choose 1024 only for example. If you have an estimate of average line length you could just use that value times 2.

Since you have no idea whatsoever about the possible upper bound for the line length, the obvious solution would be to loop over the file:

for line in fh:
    pass
last = line

You don't need to bother with the binary flag you could just use open(fname).

ETA: Since you have many files to work on, you could create a sample of couple of dozens of files using random.sample and run this code on them to determine length of last line. With an a priori large value of the position shift (let say 1 MB). This will help you to estimate the value for the full run.

SilentGhost
As long as the lines aren't longer than 1024 characters.
FogleBird
There is no guarantee that the lines aren't longer than 1024 characters, there may be some other junk besides the timestamps on the line.
pasbino
@pasbino: do you have *some* upper bound?
SilentGhost
@pasbino: You can still use a similar approach in a loop until you find a full line.
FogleBird
Unfortunately, I have not seen every line of these files. From a quick glance some of these lines seem extremely long. I don't think I can estimate an upper bound safely.
pasbino
@pasbino: 1 MB? there is always possibility to check for EOL character in the cut off chunk and cut more.
SilentGhost
Hmmmmm thats what I feared. Currently I loop over the whole file and it takes a while. But I guess without a line length upper bound there is no faster way.
pasbino
The files are about 150 MB's in size
pasbino
@pasbino: 1 MB was an example of the length of last string.
SilentGhost
Uhmmm so depending on the file its different. I just checked on a few and it seems like they're only a few kilobytes
pasbino
@pasbine: see my edit.
SilentGhost
Sorry for so many questions but how does the seek work in your first example? It means set the file's current position to 1024 bytes from the end of the file?
pasbino
@pasbino: yes. [docs](http://docs.python.org/library/io.html?highlight=seek#io.IOBase.seek) has more information.
SilentGhost
A: 

Can you use unix commands? I think using head -1 and tail -n 1 are probably the most efficient methods. Alternatively, you could use a simple fid.readline() to get the first line and fid.readlines()[-1], but that may take too much memory.

beitar
Hmm would creating a subprocess to execute these commands be the most efficient way then?
pasbino
If you do have unix then `os.popen("tail -n 1 %s" % filename).read()` gets the last line nicely.
Michael Dunn
A: 

Getting the first line is trivially easy. For the last line, presuming you know an approximate upper bound on the line length, os.lseek some amount from SEEK_END find the second to last line ending and then readline() the last line.

msw
I do not have an approximate upper bound on line length
pasbino
+1  A: 

Here's a modified version of SilentGhost's answer that will do what you want.

with open(fname, 'rb') as fh:
    first = next(fh)
    offs = -100
    while True:
        fh.seek(offs, 2)
        lines = fh.readlines()
        if len(lines)>1:
            last = lines[-1]
            break
        offs *= 2
    print first
    print last

No need for an upper bound for line length here.

m01