27

I have a two requirements .

First Requirement-I want to read the last line of a file and assign the last value to a variable in python.

Second Requirement-

Here is my sample file.

<serviceNameame="demo" wsdlUrl="demo.wsdl" serviceName="demo"/>
<context:property-placeholder location="filename.txt"/>

From this file I want to read the content i.e filename.txt which will be after <context:property-placeholder location= ..And want to assign that value to a variable in python.

Eugene Yarmash
  • 131,677
  • 37
  • 301
  • 358
techi
  • 271
  • 1
  • 3
  • 4
  • See [Read a file in reverse order using python](https://stackoverflow.com/questions/2301789/read-a-file-in-reverse-order-using-python#23646049) – jq170727 Sep 16 '17 at 22:25

7 Answers7

69

A simple solution, which doesn't require storing the entire file in memory (e.g with file.readlines() or an equivalent construct):

with open('filename.txt') as f:
    for line in f:
        pass
    last_line = line

For large files it would be more efficient to seek to the end of the file, and move backwards to find a newline, e.g.:

import os

with open('filename.txt', 'rb') as f:
    try:  # catch OSError in case of a one line file 
        f.seek(-2, os.SEEK_END)
        while f.read(1) != b'\n':
            f.seek(-2, os.SEEK_CUR)
    except OSError:
        f.seek(0)
    last_line = f.readline().decode()

Note that the file has to be opened in binary mode, otherwise, it will be impossible to seek from the end.

Eugene Yarmash
  • 131,677
  • 37
  • 301
  • 358
  • The `seek()` approach is blazingly fast, but not as easy to understand what it's doing. The `for` loop approach is not too slow and easy to understand. However, the `readlines()` approach of the next highest voted answer is slightly faster than the `for` approach here. – Mr. Lance E Sloan Mar 25 '20 at 19:08
  • 3
    @L S If you try it on a large enough file (e.g. a few megabytes), `file.readlines()` will be slower than a simple `for` loop. With larger files, it may also exhaust your available memory. – Eugene Yarmash Mar 29 '20 at 18:52
  • 1
    @EugeneYarmash This is a lovely answer for getting the last line of the file. Typically, I don't want the last line but the second or third last line of a file so I tried to generalise this into a function by changing the -2 to ''-n" but I'm getting variable answers and I think it's because it's in bytes and I haven't quite understood the code properly. n=2 does give the last line and n=3 does actually give me the 2nd last line but n=4 gives me the 9th last line. Do you know what I've done wrong here? – jpmorr May 25 '20 at 10:33
  • 1
    @jpmorr I'd think you wouldn't want to change the -2s, I believe those are moving you back one character at a time (when combined with the f.read(1)... if you change them to larger values, you skip characters, and so would get erratic results. Instead I'd modify the while loop to include a count of matches so it runs until it has matched n number of times. (basically while matches < n: f.seek(-2, os.SEEK_CUR) if f.read(1) != b'\n': matches += 1 – JeopardyTempest Oct 13 '20 at 20:28
  • I wonder if this could be sped up even slightly more in some situations (plus be well-suited to adapt to handle alternate encodings that aren't one byte per character if that can be any issue some deal with??) by instead adding the f.seek returns to a running string as you go rather than doing the readline after. I imagine it might depend upon the situation. – JeopardyTempest Oct 13 '20 at 20:41
35

Why don't you just read all the lines and store the last line to the variable?

with open('filename.txt', 'r') as f:
    last_line = f.readlines()[-1]
Alex Waygood
  • 4,796
  • 3
  • 14
  • 41
gpopides
  • 634
  • 5
  • 11
  • This is a good approach. It's not very slow, it's easier to understand than a `seek()` approach, and when wrapped in a `with` block, there's no need to explicitly call `close()` on the file. – Mr. Lance E Sloan Mar 25 '20 at 19:10
  • 19
    @LS In what way is this efficient? If your file has millions of lines, you have to read millions of lines. Anything besides `seek` or a method that starts at the end of the file is grossly inefficient. – ThomasFromUganda Apr 16 '20 at 17:33
4

Example from https://docs.python.org/3/library/collections.html

from collections import deque

def tail(filename, n=10):
    'Return the last n lines of a file'
    with open(filename) as f:
        return deque(f, n) 
Paal Pedersen
  • 695
  • 1
  • 7
  • 10
  • This will read the whole file line by line. This is memory efficient, but won't be quick for large files. – Austin Aug 21 '20 at 18:39
  • how about large files you need to think about how to do this efficiently for large files and small memory fingerprint – Walid Jan 23 '21 at 20:21
4

Do a size check and seek backwards a certain number of bytes from the end of the file if it contains at least that many bytes:

  with open(filename, 'rb') as myfile:
      if os.path.getsize(filename) > 200:
         myfile.seek(-200, 2)
      line = myfile.readlines()[-1].decode("utf-8")

Opening in binary mode is necessary in python3, which can't do nonzero end-relative seeks. myfile.seek(-200, 2) will place the current file pointer 200 characters before the end of the file (2), then the last line [-1] is taken from readlines() and decoded.

Casey Jones
  • 147
  • 8
3

He's not just asking how to read lines in the file, or how to read the last line into a variable. He's also asking how to parse out a substring from the last line, containing his target value.

Here is one way. Is it the shortest way? No, but if you don't know how to slice strings, you should start by learning each built-in function used here. This code will get what you want:

# Open the file
myfile = open("filename.txt", "r")
# Read all the lines into a List
lst = list(myfile.readlines())
# Close the file
myfile.close()
# Get just the last line
lastline = lst[len(lst)-1]
# Locate the start of the label you want, 
# and set the start position at the end 
# of the label:
intStart = lastline.find('location="') + 10
# snip off a substring from the 
# target value to the end (this is called a slice):
sub = lastline[intStart:]
# Your ending marker is now the 
# ending quote (") that is located 
# at the end of your target value.
# Get it's index.
intEnd = sub.find('"')
# Finally, grab the value, using 
# another slice operation.
finalvalue = sub[0:intEnd]
print finalvalue

The print command output should look like this:

filename.txt

Topics covered here:

  • Reading text files
  • Making a Python List of lines from the content, to make it easy to get the last line using the zero-based index len(List) -1.
  • Using find to get index locations of a string within a string
  • Using slice to obtain substrings

All of these topics are in the Python documentation - there is nothing extra here, and no imports are needed to use the built-in functions that were used here.

Cheers,
-=Cameron

3

On systems that have a tail command, you could use tail, which for large files would relieve you of the necessity of reading the entire file.

from subprocess import Popen, PIPE
f = 'yourfilename.txt'
# Get the last line from the file
p = Popen(['tail','-1',f],shell=False, stderr=PIPE, stdout=PIPE)
res,err = p.communicate()
if err:
    print (err.decode())
else:
    # Use split to get the part of the line that you require
    res = res.decode().split('location="')[1].strip().split('"')[0]
    print (res)

Note: the decode() command is only required for python3

res = res.split('location="')[1].strip().split('"')[0]

would work for python2.x

Rolf of Saxony
  • 19,475
  • 3
  • 34
  • 50
0

You can read and edit all the line doing something like:

file = open('your_file.txt', 'r')
read_file = file.readlines()
file.close()

file1 = open('your_file.txt', 'w')

var = 'filename.txt'

for lec in range(len(read_file)):
    if lec == 1:
        file1.write('<context:property-placeholder location="%s"/>' % var)
    else:
        file1.write(read_file[lec])
file1.close()
Mauricio Cortazar
  • 3,693
  • 2
  • 15
  • 27
  • Thanks for the reply. .Will it create a new file(filename.txt) in the present directory. If yes ..I don't want to create a new file in the present directory. – techi Sep 16 '17 at 22:31
  • Thanks for the reply. .Will it create a new file(filename.txt) in the present directory. If yes ..I don't want to create a new file in the present directory. – techi Sep 16 '17 at 22:36
  • no, it wont, is just editing the existing one. To understand, first we read and then we write it. I forgot something, close file1.close() – Mauricio Cortazar Sep 16 '17 at 22:37