user3342233
user3342233

Reputation: 17

Making tail function: Reversing lines in a file

I'm trying to define a function that outputs the last n lines in a file. The function below seems to mostly work, aside from the fact that the first two lines in fReverse are being joined and I can't figure out why...

example: (I tried putting these in blockquotes instead of code, but it ruins the line formatting)

f =

Darkly I gaze into the days ahead,
And see her might and granite wonders there,
Beneath the touch of Time’s unerring hand,
Like priceless treasures sinking in the sand.  

fReverse =

Like priceless treasures sinking in the sand.Beneath the touch of Time’s unerring hand,
And see her might and granite wonders there,
Darkly I gaze into the days ahead,

Code:

def tail(filename, nlines):
    '''Returns a list containing the last n lines of the file.'''
    f = open(filename, 'r')
    fReverse = open('output.txt', 'w')
    fReverse.writelines(reversed(f.readlines()))
    fReverse.close()
    f.close()
    fReverse = open('output.txt', 'r')
    listFile = []
    for i in range(1,nlines+1):
        listFile.append(fReverse.readline(),)
    fReverse.close()
    return listFile

fname = raw_input('What is the name of the file? ')
lines = int(raw_input('Number of lines to display? '))
print "The last %d lines of the file are: \n%s" % (lines, ''.join(tail(fname, lines)))

Upvotes: 0

Views: 129

Answers (4)

BeetDemGuise
BeetDemGuise

Reputation: 974

This function can be simplified down quite a bit:

def tail(filename, number_lines):
    with open(filename, 'r') as file:
        with open('output.txt', 'w') as output:
            reversed_lines = file.readlines()[::-1]
            output.write('\n'.join([line.strip() for line in reversed_lines]))

    return reversed_lines[:number_lines-1]

Upvotes: 1

zhangxaochen
zhangxaochen

Reputation: 34047

That's because the last line has no \n with it at the end ;P

You can try:

lines = reversed([l.strip()+'\n' for l in f])
fReverse.writelines(lines)

Upvotes: 0

Andrew Clark
Andrew Clark

Reputation: 208615

The issue here is that the last line of your file does not end with a newline character. So f.readlines() will be something like the following (note that the final entry does not have the \n):

['Darkly I gaze into the days ahead,\n',
 'And see her might and granite wonders there,\n',
 'Beneath the touch of Time’s unerring hand,\n',
 'Like priceless treasures sinking in the sand.']

So when you reverse this you end up writing to the file your first "line" doesn't actually write a \n and fReverse.writelines() doesn't add a line ending automatically. To fix this, just check to see if the last line from f.readlines() ends with \n and add it if necessary:

def tail(filename, nlines):
    '''Returns a list containing the last n lines of the file.'''
    f = open(filename, 'r')
    fReverse = open('output.txt', 'w')
    lines = f.readlines()
    if not lines[-1].endswith('\n'):
        lines[-1] += '\n'
    fReverse.writelines(reversed(lines))
    fReverse.close()
    f.close()
    fReverse = open('output.txt', 'r')
    listFile = []
    for i in range(1,nlines+1):
        listFile.append(fReverse.readline(),)
    fReverse.close()
    return listFile

Upvotes: 0

Jon Clements
Jon Clements

Reputation: 142216

Easier to use a deque here:

To reverse the whole file:

from collections import deque

with open('file') as fin:
    reversed_lines = deque()
    reversed_lines.extendleft(fin)

To display the last n (but iterating through all lines first):

with open('file') as fin:
    last4 = deque(fin, 4)

Upvotes: 4

Related Questions