95
fp = open("a.txt")
#do many things with fp

c = fp.read()
if c is None:
    print 'fp is at the eof'

Besides the above method, any other way to find out whether is fp is already at the eof?

Alcott
  • 17,905
  • 32
  • 116
  • 173
  • 6
    It's worth taking a look at the [``with`` statement](http://docs.python.org/reference/compound_stmts.html#the-with-statement) for opening files - it handles closing and exceptions for you nicely, and reads well. – Gareth Latty Apr 13 '12 at 12:19

22 Answers22

91

fp.read() reads up to the end of the file, so after it's successfully finished you know the file is at EOF; there's no need to check. If it cannot reach EOF it will raise an exception.

When reading a file in chunks rather than with read(), you know you've hit EOF when read returns less than the number of bytes you requested. In that case, the following read call will return the empty string (not None). The following loop reads a file in chunks; it will call read at most once too many.

assert n > 0
while True:
    chunk = fp.read(n)
    if chunk == '':
        break
    process(chunk)

Or, shorter:

for chunk in iter(lambda: fp.read(n), ''):
    process(chunk)
Fred Foo
  • 355,277
  • 75
  • 744
  • 836
  • 4
    Yes, you're right. So there is no effective way to check whether `eof` is reached? – Alcott Apr 13 '12 at 11:59
  • 1
    @Alcott: there's aix's method for ordinary files. When reading in chunks, say with `fp.read(n)`, you'll know you've hit EOF when that returns less than `n` characters. – Fred Foo Apr 13 '12 at 12:01
  • 3
    Unless you have some reason to process a file in chunks, it's generally more natural to process it line by line, which python provides as files are iterators - so you can just do ``for line in file: ...`` and let the for loop deal with it for you. – Gareth Latty Apr 13 '12 at 12:18
  • 19
    According to the [BufferedIOBase doc](http://docs.python.org/3/library/io.html#io.BufferedIOBase): "For interactive raw streams (tty/terminal), a short result does not imply that EOF is imminent." – Quentin Pradet May 02 '13 at 14:01
  • 1
    what happens if the file is exactly the number of bytes requested? – Nelson Osacky Nov 17 '13 at 07:25
  • @nosacky: in that case the loop as previously posted would call `process` on an empty string. I just changed it. – Fred Foo Nov 17 '13 at 13:07
  • 5
    @larsmans just used this, thanks! Though mine was for a binary stream, I should note here that `if chunk == '':` only works for literal string streams, `if chunk == b'':` is needed for binary streams, note the extra b. – matrixanomaly Jul 18 '15 at 07:28
  • 1
    Is your statement accurate in the case where a block of size n is requested? Documentation says that read(n) can return fewer bytes than requested if the OS returns fewer bytes. It does not say that this indicates EOF in this case. – angainor Jun 29 '16 at 11:58
  • *"when `read` returns less than the number of bytes you requested"*: perhaps, but you don't check for number of bytes returned, you look for an empty string. Then: *"In that case, the following `read` call will return the empty string"*, so if you request 100 bytes and get 99, then it will discard the 99 and return an empty string? Along with @QuentinPradet's comment about what the docs say, it seems this solution works perhaps *most* of the time but is not guaranteed behavior. In lieu of this, I suggest [Blake's newer answer](https://stackoverflow.com/a/24738688/3358272) is more appropriate. – r2evans Nov 29 '17 at 18:08
61

The "for-else" design is often overlooked. See: Python Docs "Control Flow in Loop":

Example

with open('foobar.file', 'rb') as f:
    for line in f:
        foo()

    else:
        # No more lines to be read from file
        bar()
BigMan73
  • 1,344
  • 15
  • 14
BeepBoop
  • 1,282
  • 10
  • 12
  • 49
    There is literally no point to this `else:`. Not writing it and just having `bar()` works the same. `else` only makes a difference if you use `break`. – Artyer Jan 25 '18 at 17:39
  • 2
    Someone might read this and care :) I did not know you could iterate over f line-by-line (even in binary mode!). I'm not a fan of else: there's no point to it, it just adds a line and more indented code. Its purpose and behavior is confusing just like finally in try/except. – Jacob Bruinsma Sep 29 '20 at 14:17
35

I'd argue that reading from the file is the most reliable way to establish whether it contains more data. It could be a pipe, or another process might be appending data to the file etc.

If you know that's not an issue, you could use something like:

f.tell() == os.fstat(f.fileno()).st_size
NPE
  • 486,780
  • 108
  • 951
  • 1,012
  • agreed if you call read() and you are at EOF its going to return `''` – krystan honour Apr 13 '12 at 11:57
  • 7
    I prefer `fh.seek(0, 2); file_size = fh.tell(); fh.seek(0)` beforehand and then `fh.tell() == file_size` later on. Is there an advantage to doing it your way? NOTE: I would certainly suggest caching the size to a variable and not calling `os.fstat` on every loop. – Bruno Bronosky Mar 28 '17 at 15:57
  • 4
    Note that this won't work if the file is open in text mode: `f.tell()` gives you the file position in characters and `os.fstat(f.fileno()).st_size` gives you the file length in bytes. @BrunoBronosky's method will work, though. – rmalouf May 19 '17 at 21:05
31

As python returns empty string on EOF, and not "EOF" itself, you can just check the code for it, written here

f1 = open("sample.txt")

while True:
    line = f1.readline()
    print line
    if ("" == line):
        print "file finished"
        break;
joanis
  • 10,635
  • 14
  • 30
  • 40
tingtong
  • 335
  • 3
  • 2
  • 13
    An empty line in the file breaks this algorithm. – Leonardo Raele Apr 30 '20 at 20:35
  • 19
    @LeonardoRaele: an empty line would cause `readline` to return `"\n"`. It only returns an empty string if the file is actually at EOF. – randomdude999 Sep 02 '20 at 20:05
  • 2
    Why not `if not line: break` ? – hochl Mar 05 '21 at 14:03
  • What if the file doesn't end in a `\n`, I guess `readline()` will add an `\n` in that case? (to lazy to check atm., but this detail make the API a bit confusing. Would be nice if the doc where clearer) – olejorgenb Jul 02 '22 at 11:13
  • 1
    According to [the docs](https://docs.python.org/3/tutorial/inputoutput.html#methods-of-file-objects): *`f.readline()` reads a single line from the file; a newline character (`\n`) is left at the end of the string, and is only omitted on the last line of the file if the file doesn’t end in a newline. This makes the return value unambiguous; if `f.readline()` returns an empty string, the end of the file has been reached, while a blank line is represented by `'\n'`, a string containing only a single newline.* – BenMorel Jul 11 '22 at 21:37
  • or an empty byte if you're in "wb" mode – france1 Jan 12 '23 at 15:41
17

When doing binary I/O the following method is useful:

while f.read(1):
    f.seek(-1,1)
    # whatever

The advantage is that sometimes you are processing a binary stream and do not know in advance how much you will need to read.

user545424
  • 15,713
  • 11
  • 56
  • 70
  • How does this tell you if you are at EOF? – GreenAsJade Nov 12 '14 at 13:45
  • @GreenAsJade, `f.read(1)` will return the empty string at EOF. – user545424 Nov 12 '14 at 17:19
  • Huh! And ... is the seek essential, and not just part of whatever? What's it's role? – GreenAsJade Nov 12 '14 at 21:47
  • When you use `f.read(1)` and the file is not at `EOF`, then you just read one byte, so the `f.seek(-1,1)` tells the file to move back one byte. – user545424 Nov 12 '14 at 22:23
  • I see, so this is a "read one byte until the end" loop - obvious now I know what it says! – GreenAsJade Nov 13 '14 at 01:25
  • what if that byte you read evaluates as FALSE? would a zero do that? – Chris Sep 01 '17 at 11:12
  • 1
    @Chris, as far as I know any non empty string will always evaluate to True. You can check this in the interpreter by running `bool('\0')`. – user545424 Sep 01 '17 at 14:29
  • this is an endless loop – dklovedoctor Jan 22 '18 at 10:02
  • @dklovedoctor, the idea is that you would `f.read([bytes])` in the loop to read your data. For example, if you are reading a binary file with data grouped in chunks of 100 bytes, the inside of the loop would be `data = f.read(100)`. – user545424 Jan 23 '18 at 19:14
  • I changed the comment to reflect the content of your reply – dklovedoctor Jan 27 '18 at 05:37
  • Note that this does not work for streams like sys.stdin. I'm trying to do `for line in sys.stdin` and `if something or eof: some_actions`. Because I can't do `or eof` and because `some_actions` is 13 lines (that'd be a lot of duplicate code), I guess the only solution (in python) is to make `some_actions` a function and call the function both inside and after the loop. – Luc Apr 22 '18 at 13:26
12

Here is a way to do this with the Walrus Operator (new in Python 3.8)

f = open("a.txt", "r")

while (c := f.read(n)):
    process(c)

f.close()

Useful Python Docs (3.8):

Walrus operator: https://docs.python.org/3/whatsnew/3.8.html#assignment-expressions

Methods of file objects: https://docs.python.org/3/tutorial/inputoutput.html#methods-of-file-objects

Daniel Mitchell
  • 136
  • 2
  • 5
9

You can compare the returned value of fp.tell() before and after calling the read method. If they return the same value, fp is at eof.

Furthermore, I don't think your example code actually works. The read method to my knowledge never returns None, but it does return an empty string on eof.

Lauritz V. Thaulow
  • 49,139
  • 12
  • 73
  • 92
  • You can not use `fp.tell()`, for example, if it is in an iteration state: `OSError: telling position disabled by next() call` – Andry Oct 03 '19 at 15:54
8

read returns an empty string when EOF is encountered. Docs are here.

01100110
  • 2,294
  • 3
  • 23
  • 32
8
f=open(file_name)
for line in f:
   print line
NorthCat
  • 9,643
  • 16
  • 47
  • 50
samba
  • 869
  • 4
  • 12
  • 20
  • very pythonic and no additional tests – fcm Apr 06 '19 at 13:39
  • 1
    When using `f = open(...)` rather than `with open(...) as f`, you also should make sure to call `f.close()` when you're finished or there can be unintended side effects – Lovethenakedgun May 14 '19 at 04:19
8

I really don't understand why python still doesn't have such a function. I also don't agree to use the following

f.tell() == os.fstat(f.fileno()).st_size

The main reason is f.tell() doesn't likely to work for some special conditions.

The method works for me is like the following. If you have some pseudocode like the following

while not EOF(f):
     line = f.readline()
     " do something with line"

You can replace it with:

lines = iter(f.readlines())
while True:
     try:
        line = next(lines)
        " do something with line"
     except StopIteration:
        break

This method is simple and you don't need to change most of you code.

mach6
  • 316
  • 5
  • 4
4

If file is opened in non-block mode, returning less bytes than expected does not mean it's at eof, I'd say @NPE's answer is the most reliable way:

f.tell() == os.fstat(f.fileno()).st_size

ymattw
  • 1,129
  • 10
  • 7
4

Python doesn't have built-in eof detection function but that functionality is available in two ways: f.read(1) will return b'' if there are no more bytes to read. This works for text as well as binary files. The second way is to use f.tell() to see if current seek position is at the end. If you want EOF testing not to change the current file position then you need bit of extra code.

Below are both implementations.

Using tell() method

import os

def is_eof(f):
  cur = f.tell()    # save current position
  f.seek(0, os.SEEK_END)
  end = f.tell()    # find the size of file
  f.seek(cur, os.SEEK_SET)
  return cur == end

Using read() method

def is_eof(f):
  s = f.read(1)
  if s != b'':    # restore position
    f.seek(-1, os.SEEK_CUR)
  return s == b''

How to use this

while not is_eof(my_file):
    val = my_file.read(10)

Play with this code.

Shital Shah
  • 63,284
  • 17
  • 238
  • 185
2

The Python read functions will return an empty string if they reach EOF

mensi
  • 9,580
  • 2
  • 34
  • 43
2
f = open(filename,'r')
f.seek(-1,2)     # go to the file end.
eof = f.tell()   # get the end of file location
f.seek(0,0)      # go back to file beginning

while(f.tell() != eof):
    <body>

You can use the file methods seek() and tell() to determine the position of the end of file. Once the position is found, seek back to the file beginning

Newstein
  • 141
  • 8
1

You can use tell() method after reaching EOF by calling readlines() method, like this:

fp=open('file_name','r')
lines=fp.readlines()
eof=fp.tell() # here we store the pointer
              # indicating the end of the file in eof
fp.seek(0) # we bring the cursor at the begining of the file
if eof != fp.tell(): # we check if the cursor
     do_something()  # reaches the end of the file
progmatico
  • 4,714
  • 1
  • 16
  • 27
wamba
  • 11
  • 1
1

Reading a file in batches of BATCH_SIZE lines (the last batch can be shorter):

BATCH_SIZE = 1000  # lines

with open('/path/to/a/file') as fin:
    eof = False
    while eof is False:
        # We use an iterator to check later if it was fully realized. This
        # is a way to know if we reached the EOF.
        # NOTE: file.tell() can't be used with iterators.
        batch_range = iter(range(BATCH_SIZE))
        acc = [line for (_, line) in zip(batch_range, fin)]

        # DO SOMETHING WITH "acc"

        # If we still have something to iterate, we have read the whole
        # file.
        if any(batch_range):
            eof = True
boechat107
  • 1,654
  • 14
  • 24
  • This is the best answer for [this question](https://stackoverflow.com/questions/5832856/how-to-read-file-n-lines-at-a-time) – Jeff Tilton May 06 '22 at 12:58
1

Get the EOF position of the file:

def get_eof_position(file_handle):
    original_position = file_handle.tell()
    eof_position = file_handle.seek(0, 2)
    file_handle.seek(original_position)
    return eof_position

and compare it with the current position: get_eof_position == file_handle.tell().

0

Although I would personally use a with statement to handle opening and closing a file, in the case where you have to read from stdin and need to track an EOF exception, do something like this:

Use a try-catch with EOFError as the exception:

try:
    input_lines = ''
    for line in sys.stdin.readlines():
        input_lines += line             
except EOFError as e:
    print e
Blairg23
  • 11,334
  • 6
  • 72
  • 72
0

I use this function:

# Returns True if End-Of-File is reached
def EOF(f):
    current_pos = f.tell()
    file_size = os.fstat(f.fileno()).st_size
    return current_pos >= file_size
Alex Medveshchek
  • 501
  • 4
  • 12
0

This code will work for python 3 and above

file=open("filename.txt")   
f=file.readlines()   #reads all lines from the file
EOF=-1   #represents end of file
temp=0
for k in range(len(f)-1,-1,-1):
    if temp==0:
        if f[k]=="\n":
            EOF=k
        else:
            temp+=1
print("Given file has",EOF,"lines")
file.close()
Xcalibur
  • 1
  • 1
0

You can try this code:

import sys
sys.stdin = open('input.txt', 'r') # set std input to 'input.txt'

count_lines = 0
while True:
    try: 
        v = input() # if EOF, it will raise an error
        count_lines += 1
    except EOFError:
        print('EOF', count_lines) # print numbers of lines in file
        break
Nhat Nguyen Duc
  • 432
  • 2
  • 10
-5

You can use below code snippet to read line by line, till end of file:

line = obj.readline()
while(line != ''):
    # Do Something
    line = obj.readline()
Assem
  • 11,574
  • 5
  • 59
  • 97
A R
  • 2,697
  • 3
  • 21
  • 38